Skip to content
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
### Breaking

### Features
* generic_adder: add support for templated http urls

### Improvements

Expand Down
22 changes: 16 additions & 6 deletions logprep/abc/getter.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,12 @@ def get_json(self) -> dict | list:
content = self._resolve_content(raw)
return self._parse_json(content)

def get_collection(self) -> dict | list:
def get_collection(self, content_field: str | None = None) -> dict | list:
"""Gets and parses the raw content to yaml or json"""
content = self._resolve_content_by_content_type()

content = Getter._apply_content_field(content, content_field)

if isinstance(content, str):
content = self._parse_yaml_or_json(content)

Expand All @@ -143,18 +145,26 @@ def _parse_newline_separated_list(content: str) -> list:
"""Helper which tries to convert content to list"""
return content.splitlines()

def get_list(self, content_field: str | None = None) -> list:
"""Gets list and fails otherwise"""

content = self._resolve_content_by_content_type()

@staticmethod
def _apply_content_field(
content: dict | list | str, content_field: str | None = None
) -> dict | list | str:
if isinstance(content, dict) and content_field is not None:
content = content[content_field]
elif content_field is not None:
raise ValueError(
f"Expected mapping type when content_field is set, got {type(content)}"
)

return content

def get_list(self, content_field: str | None = None) -> list:
"""Gets list and fails otherwise"""

content = self._resolve_content_by_content_type()

content = Getter._apply_content_field(content, content_field)
Comment on lines +161 to +166

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add this to get_dict as well? Not related to your PR, but it would make sense to add support to all three high-level interface methods in the same way.

Also, my personal preference would be:

Suggested change
def get_list(self, content_field: str | None = None) -> list:
"""Gets list and fails otherwise"""
content = self._resolve_content_by_content_type()
content = Getter._apply_content_field(content, content_field)
def get_list(self, content_field: str | None = None) -> list:
"""Gets list and fails otherwise"""
content = self._resolve_content_by_content_type()
content = Getter._apply_content_field(content, content_field)

(so without the line break)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure ill fix the line breaks, but adding this to get_dict somehow feels wrong to me, because we just have to do this here and for collection because we want to return a list, but in the dict, we can just get the content_field value like you would normally in a dict. Whats your opinion on this?


if isinstance(content, str):
content = self._parse_newline_separated_list(content)

Expand Down
23 changes: 22 additions & 1 deletion logprep/ng/processor/generic_adder/processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,13 @@
"""

import typing
from typing import Sequence

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
from typing import Sequence
from collections.abc import Sequence


from logprep.ng.abc.processor import Processor
from logprep.processor.base.exceptions import ProcessingWarning
from logprep.processor.base.rule import Rule
from logprep.processor.generic_adder.rule import GenericAdderRule
from logprep.util.getter import RefreshableGetter
from logprep.util.helper import add_fields_to


Expand All @@ -37,8 +40,26 @@ class GenericAdder(Processor):

rule_class = GenericAdderRule

@property
def _rules(self) -> Sequence[GenericAdderRule]:
"""Returns all rules"""
return typing.cast(Sequence[GenericAdderRule], self.rules)
Comment on lines +43 to +46

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not "rules"?

Suggested change
@property
def _rules(self) -> Sequence[GenericAdderRule]:
"""Returns all rules"""
return typing.cast(Sequence[GenericAdderRule], self.rules)
@property
def rules(self) -> Sequence[GenericAdderRule]:
"""Returns all rules"""
return typing.cast(Sequence[GenericAdderRule], super().rules)

like in

    @property
    def rules(self) -> Sequence[PseudonymizerRule]:
        """Returns all rules"""
        return typing.cast(Sequence[PseudonymizerRule], super().rules)

and other sites


async def setup(self):
await super().setup()
for rule in self._rules:
rule.init_generic_adder(self._job_tag_for_cleanup)

def _apply_rules(self, event: dict, rule: Rule) -> None:
rule = typing.cast(GenericAdderRule, rule)
items_to_add = rule.add

try:
items_to_add = rule.add(event)
except Exception as error:
raise ProcessingWarning(str(error), rule, event) from error
if items_to_add:
add_fields_to(event, items_to_add, rule, rule.merge_with_target, rule.overwrite_target)

def _shut_down(self) -> None:
RefreshableGetter.remove_callbacks_for_tag(self._job_tag_for_cleanup)
return super()._shut_down()
26 changes: 22 additions & 4 deletions logprep/processor/generic_adder/processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,12 @@
"""

import typing
from typing import Sequence

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
from typing import Sequence
from collections.abc import Sequence


from logprep.abc.processor import Processor
from logprep.processor.base.rule import Rule
from logprep.processor.base.exceptions import ProcessingWarning
from logprep.processor.generic_adder.rule import GenericAdderRule
from logprep.util.getter import RefreshableGetter
from logprep.util.helper import add_fields_to


Expand All @@ -37,8 +39,24 @@ class GenericAdder(Processor):

rule_class = GenericAdderRule

def _apply_rules(self, event: dict, rule: Rule):
rule = typing.cast(GenericAdderRule, rule)
items_to_add = rule.add
@property
def _rules(self) -> Sequence[GenericAdderRule]:
"""Returns all rules"""
return typing.cast(Sequence[GenericAdderRule], self.rules)

def setup(self):
super().setup()
for rule in self._rules:
rule.init_generic_adder(self._job_tag_for_cleanup)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we really need this external init and would like to avoid it. The list_comparison has it, as the list_search_base_path comes from the processor and is relevant for the function of the rule. Here rule could generate it's own job tag, couldn't it?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we really need this external init and would like to avoid it. The list_comparison has it, as the list_search_base_path comes from the processor and is relevant for the function of the rule. Here rule could generate it's own job tag, couldn't it?

Yes we do, I forgot to add the _shut_down function here, this makes it clearer why we need to init here

@mhoff mhoff Jul 24, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mh, I see where you are coming from. Alright, we can keep this for consistency with other processors (namely list_comparison).

But I still think that the job_tag and managing callbacks is rather an implementation detail of the rule, which it should handle itself. I think it would be consequential, that processor.setup() calls rule.setup() and processor.shut_down() calls rule.shut_down(), whereas managing callbacks is completely handled by the rule.

Currently we are calling an init (which has setup-like semantics) on the rule but basically handle the shutdown on the processor-level, which is inconsistent.


def _apply_rules(self, event: dict, rule: GenericAdderRule):
try:
items_to_add = rule.add(event)
except Exception as error:
raise ProcessingWarning(str(error), rule, event) from error
if items_to_add:
add_fields_to(event, items_to_add, rule, rule.merge_with_target, rule.overwrite_target)

def _shut_down(self) -> None:
RefreshableGetter.remove_callbacks_for_tag(self._job_tag_for_cleanup)
return super()._shut_down()
Loading
Loading