How to Create Custom Analyzers for Proprietary Log Formats in LogSentinelAI
You can extend LogSentinelAI to support any proprietary log format by implementing a Python module in src/logsentinelai/analyzers/ that defines Pydantic schemas for structured output, registers a prompt template in core.prompts, and delegates execution to the generic analysis drivers in core.commons.
LogSentinelAI is built around a plug-in architecture where each log type is handled by an independent analyzer module. This design allows security teams to integrate proprietary or internal log formats without modifying the core codebase. By following the established patterns in the call518/logsentinelai repository, you inherit the complete LLM processing pipeline including GeoIP enrichment, JSON schema validation, and Elasticsearch ingestion.
Understanding the Analyzer Architecture
Every analyzer follows a standardized execution flow implemented in src/logsentinelai/core/commons.py. This generic pipeline handles the heavy lifting so you only need to supply the data model and prompt logic.
- Argument parsing via
create_argument_parserfor standardized CLI interfaces. - SSH handling through
handle_ssh_argumentsfor remote log access. - Driver selection between
run_generic_batch_analysisandrun_generic_realtime_analysis. - Prompt generation using Jinja-style templates from
src/logsentinelai/core/prompts.pypopulated with log chunks and Pydantic schemas. - LLM processing via
process_log_chunk, which validates responses against your schema, enriches data with GeoIP, and ships results to Elasticsearch.
Concrete implementations reside in src/logsentinelai/analyzers/. The general_log.py file demonstrates the canonical structure that all custom analyzers should replicate.
Step-by-Step Implementation Guide
Step 1: Create the Analyzer Module
Create a new Python file under src/logsentinelai/analyzers/<mylog>.py. This module must import the core execution helpers and define the entry point function main().
Step 2: Define Pydantic Data Models
Use Pydantic models to enforce the JSON structure the LLM must return. Define enums for SeverityLevel and EventCategory, then create event and analysis classes that inherit from BaseModel. The generic drivers call analysis_schema_class.model_json_schema() to inject the schema into prompts and validate LLM outputs.
Step 3: Add a Prompt Template
In src/logsentinelai/core/prompts.py, add a constant PROMPT_TEMPLATE_<MYLOG> and a getter function get_<mylog>_prompt(). The template must include the {model_schema} placeholder, which the drivers replace at runtime with your Pydantic schema.
Step 4: Implement the Driver Logic
The main() function orchestrates the workflow using helpers from src/logsentinelai/core/commons.py:
- Build the argument parser using
create_argument_parser. - Resolve SSH configuration via
handle_ssh_arguments. - Select batch or real-time mode based on CLI arguments.
- Invoke
run_generic_batch_analysisorrun_generic_realtime_analysiswith your schema class and prompt getter.
Step 5: Register the Analyzer
Export your analyzer in src/logsentinelai/analyzers/__init__.py by importing the analysis class and adding it to the __all__ list. This makes the module discoverable to the rest of the package.
Step 6: Wire the CLI Sub-Command
To make the analyzer accessible via the unified logsentinelai command, extend src/logsentinelai/cli.py. Add a new sub-parser that forwards arguments to your analyzer's main() function.
Step 7: Configure Defaults (Optional)
For convenience, add default log paths and chunk sizes to LOG_PATHS and LOG_CHUNK_SIZES in src/logsentinelai/core/config.py. The generic drivers use get_analysis_config() to resolve these settings.
Complete Working Example: MyApp Log Analyzer
The following implementation demonstrates a production-ready analyzer for a hypothetical proprietary "MyApp" format. Save this as src/logsentinelai/analyzers/myapp_log.py:
# src/logsentinelai/analyzers/myapp_log.py
from pydantic import BaseModel, Field
from enum import Enum
from typing import Optional, List
# Prompt import
from ..core.prompts import get_myapp_log_prompt
# Core helpers
from ..core.commons import (
run_generic_batch_analysis,
run_generic_realtime_analysis,
create_argument_parser,
handle_ssh_arguments,
)
# ── Severity & Event definitions ────────────────────────────────────────
class SeverityLevel(str, Enum):
CRITICAL = "CRITICAL"
HIGH = "HIGH"
MEDIUM = "MEDIUM"
LOW = "LOW"
INFO = "INFO"
class EventCategory(str, Enum):
AUTH = "AUTH"
DATA = "DATA"
SYSTEM = "SYSTEM"
UNKNOWN = "UNKNOWN"
class MyAppEvent(BaseModel):
category: EventCategory
severity: SeverityLevel
related_logs: List[str] = Field(
min_length=1,
description="Exact log lines that triggered this event"
)
description: str
confidence_score: float = Field(ge=0.0, le=1.0)
source_ips: List[str] = Field(description="All source IPs in the chunk")
recommended_actions: List[str]
requires_human_review: bool
class MyAppAnalysis(BaseModel):
summary: str
events: List[MyAppEvent] = Field(
min_length=1,
description="At least one event must be present"
)
# Optional statistics block
total_events: int = Field(description="Total events detected")
unique_ips: int = Field(description="Number of distinct source IPs")
def main() -> int:
parser = create_argument_parser("MyApp Log Analysis")
args = parser.parse_args()
# SSH handling (optional)
ssh_cfg = handle_ssh_arguments(args)
remote_mode = "ssh" if ssh_cfg else "local"
log_type = "myapp_log"
analysis_title = "MyApp Log Analysis"
if args.mode == "realtime":
run_generic_realtime_analysis(
log_type=log_type,
analysis_schema_class=MyAppAnalysis,
prompt_template=get_myapp_log_prompt(),
analysis_title=analysis_title,
chunk_size=args.chunk_size,
log_path=args.log_path,
only_sampling_mode=args.only_sampling_mode,
sampling_threshold=args.sampling_threshold,
remote_mode=remote_mode,
ssh_config=ssh_cfg,
)
else:
run_generic_batch_analysis(
log_type=log_type,
analysis_schema_class=MyAppAnalysis,
prompt_template=get_myapp_log_prompt(),
analysis_title=analysis_title,
log_path=args.log_path,
remote_mode=remote_mode,
ssh_config=ssh_cfg,
)
return 0
if __name__ == "__main__":
raise SystemExit(main())
Add the corresponding prompt template to src/logsentinelai/core/prompts.py:
PROMPT_TEMPLATE_MYAPP_LOG = """
Expert analyst for the proprietary MyApp log format. Detect security events,
operational anomalies and business‑logic incidents.
LOG PARSING CRITICAL:
- Identify individual log entries (they may span multiple lines).
- Extract timestamps, user identifiers, IP addresses, and any structured key‑value pairs.
SEVERITY:
- CRITICAL: Data exfiltration, privilege escalation, authentication bypass.
- HIGH: Repeated failed logins, suspicious API calls.
- MEDIUM: Unusual configuration changes.
- LOW: Minor warnings.
- INFO: Normal activity (e.g., scheduled jobs, health checks).
MANDATORY EVENT RULES:
- Never leave the `events` array empty.
- Always include a complete `source_ips` list.
- If no security concerns, create an INFO event summarising traffic.
STATISTICS:
- total_events, unique_ips
LANGUAGE: {response_language}
JSON schema: {model_schema}
<LOGS BEGIN>
{logs}
<LOGS END>
"""
def get_myapp_log_prompt() -> str:
"""Return the MyApp log analysis prompt."""
return PROMPT_TEMPLATE_MYAPP_LOG
Register the analyzer in src/logsentinelai/analyzers/__init__.py:
# src/logsentinelai/analyzers/__init__.py
from .myapp_log import LogAnalysis as MyAppLogAnalysis
__all__ = [
"HTTPDAccessAnalysis",
"HTTPDServerAnalysis",
"LinuxSystemAnalysis",
"MyAppLogAnalysis",
]
Finally, expose it via the CLI in src/logsentinelai/cli.py:
myapp_parser = subparsers.add_parser(
"myapp-log",
help="Analyze proprietary MyApp logs"
)
myapp_parser.add_argument("--log-path", help="Path to MyApp log file")
myapp_parser.add_argument(
"--mode",
choices=["batch", "realtime"],
default="batch",
help="Analysis mode (default: batch)",
)
# Later in the command router:
elif args.command == "myapp-log":
from .analyzers.myapp_log import main as myapp_main
sys.argv = ["logsentinelai-myapp-log"]
if args.log_path:
sys.argv.extend(["--log-path", args.log_path])
if args.mode:
sys.argv.extend(["--mode", args.mode])
myapp_main()
Testing Your Custom Analyzer
Validate the implementation by running the analyzer against sample data:
python -m logsentinelai.analyzers.myapp_log --log-path /var/log/myapp/test.log --mode batch
Verify that the output produces valid JSON matching your Pydantic schema and that documents appear in Elasticsearch with the correct enrichment fields.
Summary
- Plug-in architecture: LogSentinelAI isolates log-type logic in
src/logsentinelai/analyzers/, allowing zero-core-code modifications for new formats. - Schema-first design: Define your output structure using Pydantic models; the generic drivers handle LLM validation and indexing automatically.
- Centralized prompts: Store Jinja-style templates in
src/logsentinelai/core/prompts.pywith the mandatory{model_schema}placeholder. - Generic drivers: Delegate execution to
run_generic_batch_analysisorrun_generic_realtime_analysisinsrc/logsentinelai/core/commons.pyto inherit SSH support, GeoIP enrichment, and error handling. - Registration: Export classes in
__init__.pyand optionally wire CLI commands incli.pyfor unified access.
Frequently Asked Questions
What Python version does LogSentinelAI require for custom analyzers?
LogSentinelAI requires Python 3.9 or higher to support the Pydantic v2 features and type hints used throughout the analyzer modules. Ensure your virtual environment matches the version specified in the repository's pyproject.toml.
Can I use an external API instead of the built-in LLM client?
Yes. While the generic drivers in core.commons use the internal LLM client configured via environment variables, you can override the process_log_chunk call within your analyzer's main() function. However, you must still return data matching your Pydantic schema to maintain compatibility with the Elasticsearch export pipeline.
How do I handle multi-line log entries in proprietary formats?
Define explicit parsing logic in your prompt template's "LOG PARSING CRITICAL" section. Instruct the LLM to identify continuation patterns (e.g., indented lines, timestamps, or stack traces). The analyzer feeds raw text chunks to the LLM; the prompt engineering determines how the model boundaries individual entries within those chunks.
Is real-time mode supported for all custom analyzers?
Yes. Any analyzer implementing the standard main() structure with argument parsing can invoke run_generic_realtime_analysis instead of the batch driver. The real-time driver uses the same schema validation and prompt generation, but streams chunks continuously from the log file tail rather than processing the entire file at once.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →