Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 44 additions & 20 deletions docs/detectors/random_detector.md
Original file line number Diff line number Diff line change
@@ -1,38 +1,62 @@
# Random Detector

The Random Detector produces randomized alerts for incoming parsed logs. It is useful for testing pipelines, alert routing, and downstream consumers without needing a real detection model.
The Random Detector inspects incoming ParserSchema instances and, according to its configuration, emits alerts with synthetic content. It can be configured to sample specific log variables, set thresholds or control alert frequency. Use it for integration testing, load testing, or as a simple example of a detector implementation.

## In/out

Input and output schemas in the pipeline

| | Schema | Description |
|------------|------------------------|--------------------|
| **Input** | [ParserSchema](../schemas.md) | Structured log |
| **Output** | [DetectorSchema](../schemas.md) | Generated alerts |

## Description
## Configuration arguments

Arguments used in the initalization of the component.

<!-- Start arguments -->
| Field | Type | Default Value| Description|
|-------|------|-----|---|
|method_type|string|random_detector|Indicates what type of method is.|
|auto_config|boolean|True|Runs the configuration step before the training process.|
|start_id|integer|10|Number use to start the unique ID generator.|
|data_use_training|integer, null|None|Data use for training, if None, training is not done.|
|data_use_configure|integer, null|None|Data use for configuration, if None, configuration is not done.|
|use_config_data_as_training|boolean|True|Combine the configure data in the training process if True.|
|parser|string|PARSER|Name of the parser used.|
|events|object|{}|Events configuration dict keyed by event_id.|
|global_instances|object|{}|Configuration for a specific instance within an event.|
<!-- End arguments -->

The detector inspects incoming ParserSchema instances and, according to its configuration, emits alerts with synthetic content. It can be configured to sample specific log variables, set thresholds or control alert frequency. Use it for integration testing, load testing, or as a simple example of a detector implementation.
## Examples

## Configuration example
Examples to use the component in the DetectMate environment.

## Service usage

To use it in [DetectMateService](https://github.com/ait-detectmate/DetectMateService), you can use the example bellow.

<!-- Start config -->
```yaml
RandomDetector:
detectors:
<COMPONENT_NAME>:
method_type: random_detector
auto_config: False
params: {}
events:
1:
test:
params: {}
variables:
- pos: 0
name: var1
params:
threshold: 0.
header_variables:
- pos: level
params: {}
auto_config: true
params:
start_id: 10
data_use_training: null
data_use_configure: null
use_config_data_as_training: true
parser: PARSER
global_instances: {}
events: {}
```
<!-- End config -->

## Library usage

## Example usage
To use it as a python script, you can follow the example bellow.

```python
--8<-- "docs/examples/detectors/random_detector.py:example"
Expand Down
58 changes: 58 additions & 0 deletions docs/examples/config/update.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
from detectmatelibrary.detectors.random_detector import RandomDetectorConfig

from detectmatelibrary.common.core import CoreConfig

import yaml


# %% Methods
def append_docs(docs: str, start_cmd: str, end_cmd: str, add: str) -> None:
start_idx = docs.index(start_cmd)
end_idx = docs.index(end_cmd)
if start_idx > end_idx:
raise Exception(f"'{start_cmd}' should be before '{end_cmd}'")

return docs[:start_idx + 1] + [add] + docs[end_idx:]


def get_arguments(config: CoreConfig) -> str:
arguments = "| Field | Type | Default Value| Description|\n|-------|------|-----|---|\n"
for arg in config.get_docs():
arguments += f"|{arg['Name']}|{arg["Type"]}|{arg["Default value"]}|{arg["Description"]}|\n"
return arguments


def config_yaml(config: CoreConfig) -> str:
pretty_yaml = yaml.dump(
config.to_dict("<COMPONENT_NAME>"), indent=4, default_flow_style=False, sort_keys=False
)
return "```yaml\n" + pretty_yaml + "```\n"


def update_docs(config: CoreConfig, doc_path: str) -> None:
try:
with open(doc_path, "r") as f:
docs = f.readlines()

docs = append_docs(
docs=docs,
start_cmd="<!-- Start arguments -->\n",
end_cmd="<!-- End arguments -->\n",
add=get_arguments(config)
)

docs = append_docs(
docs=docs,
start_cmd="<!-- Start config -->\n",
end_cmd="<!-- End config -->\n",
add=config_yaml(config)
)

with open(doc_path, "w") as f:
f.writelines(docs)
except Exception as e:
raise Exception(f"While updating {doc_path} -> {str(e)}")


# %% Documentation update
update_docs(RandomDetectorConfig(), doc_path="docs/detectors/random_detector.md")
35 changes: 30 additions & 5 deletions src/detectmatelibrary/common/_config/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

__all__ = ["ConfigMethods", "generate_detector_config", "EventsConfig", "BasicConfig"]

from pydantic import BaseModel, ConfigDict
from pydantic import BaseModel, ConfigDict, Field

from typing_extensions import Self
from typing import Any, Dict
Expand All @@ -23,10 +23,35 @@ class BasicConfig(BaseModel):

model_config = ConfigDict(extra="forbid")

method_type: str = "default_method_type"
component_type: str = "default_type"

auto_config: bool = False
method_type: str = Field(
default="default_method_type",
description="Indicates what type of method is."
)
component_type: str = Field(
default="default_type",
description="Component type that the class inherent from."
)

auto_config: bool = Field(
default=False,
description="Runs the configuration step before the training process."
)

def get_docs(self) -> list[dict[str, str]]:
docs = []
for field_na, field_info in self.model_json_schema().get("properties", {}).items():
desc = field_info.get("description", "No description provided.")
if "<$IGNORE$>" in desc:
continue
type_ = field_info.get("type", "unknown")
if 'anyOf' in field_info:
types = [item['type'] for item in field_info['anyOf'] if 'type' in item]
type_ = ", ".join(types)

docs.append({
"Name": field_na, "Type": type_, "Default value": getattr(self, field_na), "Description": desc
})
return docs

def get_config(self) -> Dict[str, Any]:
"""Return the configuration as a dictionary."""
Expand Down
21 changes: 17 additions & 4 deletions src/detectmatelibrary/common/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@


from typing import Any
from pydantic import Field

from detectmatelibrary.utils.persistency.component_interfaces import PersistencyOp

Expand Down Expand Up @@ -47,10 +48,22 @@ def __iter__(self) -> "TrainBuffer":
# Core component ################################################

class CoreConfig(BasicConfig):
start_id: int = 10
data_use_training: int | None = None
data_use_configure: int | None = None
use_config_data_as_training: bool = True
start_id: int = Field(
default=10,
description="Number use to start the unique ID generator."
)
data_use_training: int | None = Field(
default=None,
description="Data use for training, if None, training is not done."
)
data_use_configure: int | None = Field(
default=None,
description="Data use for configuration, if None, configuration is not done.",
)
use_config_data_as_training: bool = Field(
default=True,
description="Combine the configure data in the training process if True."
)


class CoreComponent(Component, FedOperations):
Expand Down
28 changes: 20 additions & 8 deletions src/detectmatelibrary/common/detector.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

from typing_extensions import override
from typing import Dict, List, Optional, Any, cast
from pydantic import Field

from detectmatelibrary.utils.persistency.component_interfaces import PersistConfig
from detectmatelibrary.utils.time_format_handler import TimeFormatHandler
Expand All @@ -36,14 +37,25 @@ def _extract_logIDs(


class CoreDetectorConfig(CoreConfig):
component_type: str = "detectors"
method_type: str = "core_detector"
parser: str = "<PLACEHOLDER>"

auto_config: bool = True
events: EventsConfig | dict[str, Any] = {}
global_instances: Dict[str, _EventInstance] = {}
persist: PersistConfig | None = None
component_type: str = Field(default="detectors", description="<$IGNORE$>")
method_type: str = Field(default="core_detector", description="<$IGNORE$>")
parser: str = Field(
default="PARSER", description="Name of the parser used."
)

auto_config: bool = Field(
default=True,
description="Runs the configuration step before the training process."
)
events: EventsConfig | dict[str, Any] = Field(
default={}, description=EventsConfig.__doc__,
)
global_instances: Dict[str, _EventInstance] = Field(
default={}, description=_EventInstance.__doc__
)
persist: PersistConfig | None = Field(
default=None, description="<$IGNORE$>"
)


class CoreDetector(CoreComponent):
Expand Down
13 changes: 8 additions & 5 deletions src/detectmatelibrary/detectors/random_detector.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from detectmatelibrary.common._config._formats import EventsConfig, Variable
from detectmatelibrary.common._config._formats import Variable

from detectmatelibrary.common.detector import CoreDetector, CoreDetectorConfig

Expand All @@ -7,14 +7,17 @@
import detectmatelibrary.schemas as schemas

from typing_extensions import override
from typing import List, Any
from typing import List

from pydantic import Field
import numpy as np


class RandomDetectorConfig(CoreDetectorConfig):
method_type: str = "random_detector"

events: EventsConfig | dict[str, Any] = {}
method_type: str = Field(
default="random_detector",
description="Indicates what type of method is."
)


class RandomDetector(CoreDetector):
Expand Down
53 changes: 52 additions & 1 deletion tests/test_common/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
)
from detectmatelibrary.common._config._formats import EventsConfig, _EventConfig
from detectmatelibrary.common._config import BasicConfig
from pydantic import ValidationError
from pydantic import ValidationError, Field
from tests.test_data import TEST_CONFIG
import pytest
import yaml
Expand All @@ -22,6 +22,57 @@ def load_test_config() -> dict:
config_test = load_test_config()


class DummyConfigDoc(BasicConfig):
hello: str | None = Field(default="Hello", description="a way to salute people")
dont_show: str = Field(default="a", description="<$IGNORE$> dont show stuff")
auto_config: bool = Field(
default=True,
description="Runs the configuration step before the training process."
)


class TestConfigDocs:
def test_get_configs(self):
docs = BasicConfig().get_docs()

assert len(docs) == 3
assert {
'Name': 'method_type',
'Type': 'string',
'Default value': 'default_method_type',
'Description': 'Indicates what type of method is.'
} in docs
assert {
'Name': 'component_type',
'Type': 'string',
'Default value': 'default_type',
'Description': 'Component type that the class inherent from.'
} in docs
assert {
'Name': 'auto_config',
'Type': 'boolean',
'Default value': False,
'Description': 'Runs the configuration step before the training process.'
} in docs

def test_inherent_class_docs(self):
docs = DummyConfigDoc().get_docs()

assert len(docs) == 4
assert {
'Name': 'auto_config',
'Type': 'boolean',
'Default value': True,
'Description': 'Runs the configuration step before the training process.'
} in docs
assert {
'Name': 'hello',
'Type': 'string, null',
'Default value': 'Hello',
'Description': 'a way to salute people'
} in docs


class TestConfigMethods:
def test_get_method(self):
config = ConfigMethods.get_method(
Expand Down
Loading