fluidattacks-core 3.0.0__tar.gz → 4.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {fluidattacks_core-3.0.0 → fluidattacks_core-4.0.0}/PKG-INFO +1 -1
- {fluidattacks_core-3.0.0 → fluidattacks_core-4.0.0}/fluidattacks_core/logging/__init__.py +18 -7
- fluidattacks_core-4.0.0/fluidattacks_core/logging/filters.py +13 -0
- {fluidattacks_core-3.0.0 → fluidattacks_core-4.0.0}/fluidattacks_core/logging/formatters.py +26 -43
- {fluidattacks_core-3.0.0 → fluidattacks_core-4.0.0}/fluidattacks_core/logging/handlers.py +6 -6
- fluidattacks_core-4.0.0/fluidattacks_core/logging/presets.py +40 -0
- fluidattacks_core-4.0.0/fluidattacks_core/logging/sources/__init__.py +147 -0
- fluidattacks_core-4.0.0/fluidattacks_core/logging/sources/types.py +13 -0
- fluidattacks_core-4.0.0/fluidattacks_core/logging/sources/utils.py +27 -0
- fluidattacks_core-4.0.0/fluidattacks_core/logging/utils.py +37 -0
- {fluidattacks_core-3.0.0 → fluidattacks_core-4.0.0}/fluidattacks_core.egg-info/PKG-INFO +1 -1
- {fluidattacks_core-3.0.0 → fluidattacks_core-4.0.0}/fluidattacks_core.egg-info/SOURCES.txt +3 -1
- {fluidattacks_core-3.0.0 → fluidattacks_core-4.0.0}/pyproject.toml +1 -1
- fluidattacks_core-4.0.0/test/test_logging.py +321 -0
- fluidattacks_core-3.0.0/fluidattacks_core/logging/filters.py +0 -17
- fluidattacks_core-3.0.0/fluidattacks_core/logging/presets.py +0 -29
- fluidattacks_core-3.0.0/fluidattacks_core/logging/types.py +0 -13
- fluidattacks_core-3.0.0/fluidattacks_core/logging/utils.py +0 -130
- fluidattacks_core-3.0.0/test/test_logging.py +0 -179
- {fluidattacks_core-3.0.0 → fluidattacks_core-4.0.0}/README.md +0 -0
- {fluidattacks_core-3.0.0 → fluidattacks_core-4.0.0}/fluidattacks_core/__init__.py +0 -0
- {fluidattacks_core-3.0.0 → fluidattacks_core-4.0.0}/fluidattacks_core/aio/__init__.py +0 -0
- {fluidattacks_core-3.0.0 → fluidattacks_core-4.0.0}/fluidattacks_core/aio/processes.py +0 -0
- {fluidattacks_core-3.0.0 → fluidattacks_core-4.0.0}/fluidattacks_core/aio/runners.py +0 -0
- {fluidattacks_core-3.0.0 → fluidattacks_core-4.0.0}/fluidattacks_core/aio/tasks.py +0 -0
- {fluidattacks_core-3.0.0 → fluidattacks_core-4.0.0}/fluidattacks_core/aio/threads.py +0 -0
- {fluidattacks_core-3.0.0 → fluidattacks_core-4.0.0}/fluidattacks_core/authz/__init__.py +0 -0
- {fluidattacks_core-3.0.0 → fluidattacks_core-4.0.0}/fluidattacks_core/authz/py.typed +0 -0
- {fluidattacks_core-3.0.0 → fluidattacks_core-4.0.0}/fluidattacks_core/authz/types.py +0 -0
- {fluidattacks_core-3.0.0 → fluidattacks_core-4.0.0}/fluidattacks_core/bugsnag/__init__.py +0 -0
- {fluidattacks_core-3.0.0 → fluidattacks_core-4.0.0}/fluidattacks_core/bugsnag/client.py +0 -0
- {fluidattacks_core-3.0.0 → fluidattacks_core-4.0.0}/fluidattacks_core/cpg/__init__.py +0 -0
- {fluidattacks_core-3.0.0 → fluidattacks_core-4.0.0}/fluidattacks_core/cpg/joern.py +0 -0
- {fluidattacks_core-3.0.0 → fluidattacks_core-4.0.0}/fluidattacks_core/filesystem/__init__.py +0 -0
- {fluidattacks_core-3.0.0 → fluidattacks_core-4.0.0}/fluidattacks_core/filesystem/defaults.py +0 -0
- {fluidattacks_core-3.0.0 → fluidattacks_core-4.0.0}/fluidattacks_core/git/__init__.py +0 -0
- {fluidattacks_core-3.0.0 → fluidattacks_core-4.0.0}/fluidattacks_core/git/classes.py +0 -0
- {fluidattacks_core-3.0.0 → fluidattacks_core-4.0.0}/fluidattacks_core/git/clone.py +0 -0
- {fluidattacks_core-3.0.0 → fluidattacks_core-4.0.0}/fluidattacks_core/git/codecommit_utils.py +0 -0
- {fluidattacks_core-3.0.0 → fluidattacks_core-4.0.0}/fluidattacks_core/git/constants.py +0 -0
- {fluidattacks_core-3.0.0 → fluidattacks_core-4.0.0}/fluidattacks_core/git/delete_files.py +0 -0
- {fluidattacks_core-3.0.0 → fluidattacks_core-4.0.0}/fluidattacks_core/git/download_file.py +0 -0
- {fluidattacks_core-3.0.0 → fluidattacks_core-4.0.0}/fluidattacks_core/git/download_repo.py +0 -0
- {fluidattacks_core-3.0.0 → fluidattacks_core-4.0.0}/fluidattacks_core/git/https_utils.py +0 -0
- {fluidattacks_core-3.0.0 → fluidattacks_core-4.0.0}/fluidattacks_core/git/py.typed +0 -0
- {fluidattacks_core-3.0.0 → fluidattacks_core-4.0.0}/fluidattacks_core/git/remote.py +0 -0
- {fluidattacks_core-3.0.0 → fluidattacks_core-4.0.0}/fluidattacks_core/git/ssh_utils.py +0 -0
- {fluidattacks_core-3.0.0 → fluidattacks_core-4.0.0}/fluidattacks_core/git/utils.py +0 -0
- {fluidattacks_core-3.0.0 → fluidattacks_core-4.0.0}/fluidattacks_core/git/warp.py +0 -0
- {fluidattacks_core-3.0.0 → fluidattacks_core-4.0.0}/fluidattacks_core/http/__init__.py +0 -0
- {fluidattacks_core-3.0.0 → fluidattacks_core-4.0.0}/fluidattacks_core/http/client.py +0 -0
- {fluidattacks_core-3.0.0 → fluidattacks_core-4.0.0}/fluidattacks_core/http/validations.py +0 -0
- {fluidattacks_core-3.0.0 → fluidattacks_core-4.0.0}/fluidattacks_core/py.typed +0 -0
- {fluidattacks_core-3.0.0 → fluidattacks_core-4.0.0}/fluidattacks_core/sarif/__init__.py +0 -0
- {fluidattacks_core-3.0.0 → fluidattacks_core-4.0.0}/fluidattacks_core/semver/__init__.py +0 -0
- {fluidattacks_core-3.0.0 → fluidattacks_core-4.0.0}/fluidattacks_core/semver/match_versions.py +0 -0
- {fluidattacks_core-3.0.0 → fluidattacks_core-4.0.0}/fluidattacks_core/serializers/__init__.py +0 -0
- {fluidattacks_core-3.0.0 → fluidattacks_core-4.0.0}/fluidattacks_core/serializers/snippet.py +0 -0
- {fluidattacks_core-3.0.0 → fluidattacks_core-4.0.0}/fluidattacks_core/serializers/syntax.py +0 -0
- {fluidattacks_core-3.0.0 → fluidattacks_core-4.0.0}/fluidattacks_core.egg-info/dependency_links.txt +0 -0
- {fluidattacks_core-3.0.0 → fluidattacks_core-4.0.0}/fluidattacks_core.egg-info/requires.txt +0 -0
- {fluidattacks_core-3.0.0 → fluidattacks_core-4.0.0}/fluidattacks_core.egg-info/top_level.txt +0 -0
- {fluidattacks_core-3.0.0 → fluidattacks_core-4.0.0}/setup.cfg +0 -0
- {fluidattacks_core-3.0.0 → fluidattacks_core-4.0.0}/test/test_aio_tasks.py +0 -0
- {fluidattacks_core-3.0.0 → fluidattacks_core-4.0.0}/test/test_extract_db.py +0 -0
- {fluidattacks_core-3.0.0 → fluidattacks_core-4.0.0}/test/test_language_detection.py +0 -0
- {fluidattacks_core-3.0.0 → fluidattacks_core-4.0.0}/test/test_make_snippet.py +0 -0
- {fluidattacks_core-3.0.0 → fluidattacks_core-4.0.0}/test/test_match_versions.py +0 -0
|
@@ -1,11 +1,21 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import logging
|
|
2
4
|
import logging.config
|
|
3
5
|
import sys
|
|
4
|
-
from
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
5
7
|
|
|
6
8
|
from fluidattacks_core.logging.presets import DATE_FORMAT, PRODUCT_LOGGING
|
|
7
|
-
from fluidattacks_core.logging.
|
|
8
|
-
|
|
9
|
+
from fluidattacks_core.logging.sources.utils import (
|
|
10
|
+
set_commit_ref_name,
|
|
11
|
+
set_commit_sha,
|
|
12
|
+
set_product_id,
|
|
13
|
+
)
|
|
14
|
+
from fluidattacks_core.logging.utils import set_telemetry_metadata
|
|
15
|
+
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from logging.config import _DictConfigArgs
|
|
18
|
+
from types import TracebackType
|
|
9
19
|
|
|
10
20
|
|
|
11
21
|
def init_uncaught_exception_logging() -> None:
|
|
@@ -28,17 +38,18 @@ def init_uncaught_exception_logging() -> None:
|
|
|
28
38
|
sys.excepthook = handle_uncaught_exception
|
|
29
39
|
|
|
30
40
|
|
|
31
|
-
def init_logging() -> None:
|
|
32
|
-
logging.config.dictConfig(PRODUCT_LOGGING)
|
|
41
|
+
def init_logging(preset: _DictConfigArgs | None = None) -> None:
|
|
42
|
+
logging.config.dictConfig(preset or PRODUCT_LOGGING)
|
|
33
43
|
init_uncaught_exception_logging()
|
|
34
44
|
|
|
35
45
|
|
|
36
46
|
__all__ = [
|
|
37
47
|
"DATE_FORMAT",
|
|
38
48
|
"PRODUCT_LOGGING",
|
|
39
|
-
"JobMetadata",
|
|
40
|
-
"get_job_metadata",
|
|
41
49
|
"init_logging",
|
|
42
50
|
"init_uncaught_exception_logging",
|
|
51
|
+
"set_commit_ref_name",
|
|
52
|
+
"set_commit_sha",
|
|
53
|
+
"set_product_id",
|
|
43
54
|
"set_telemetry_metadata",
|
|
44
55
|
]
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from fluidattacks_core.logging.sources.utils import get_environment
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class NoProductionFilter(logging.Filter):
|
|
7
|
+
def filter(self, _record: logging.LogRecord) -> bool:
|
|
8
|
+
return get_environment() != "production"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ProductionOnlyFilter(logging.Filter):
|
|
12
|
+
def filter(self, _record: logging.LogRecord) -> bool:
|
|
13
|
+
return get_environment() == "production"
|
|
@@ -2,26 +2,24 @@ import logging
|
|
|
2
2
|
import traceback
|
|
3
3
|
from datetime import UTC, datetime
|
|
4
4
|
from decimal import Decimal
|
|
5
|
-
from typing import Any
|
|
5
|
+
from typing import TYPE_CHECKING, Any
|
|
6
6
|
|
|
7
7
|
import simplejson as json
|
|
8
8
|
from pythonjsonlogger.json import JsonFormatter
|
|
9
9
|
|
|
10
|
+
from fluidattacks_core.logging.sources import (
|
|
11
|
+
BatchSource,
|
|
12
|
+
ContainerSource,
|
|
13
|
+
DefaultSource,
|
|
14
|
+
LambdaSource,
|
|
15
|
+
PipelineSource,
|
|
16
|
+
)
|
|
10
17
|
from fluidattacks_core.logging.utils import (
|
|
11
|
-
get_environment_metadata,
|
|
12
|
-
get_job_metadata,
|
|
13
|
-
get_pipeline_environment,
|
|
14
|
-
get_pipeline_metadata,
|
|
15
18
|
get_telemetry_metadata,
|
|
16
|
-
is_in_batch,
|
|
17
|
-
is_in_lambda,
|
|
18
19
|
)
|
|
19
20
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
"""
|
|
23
|
-
Default date format for logs.
|
|
24
|
-
"""
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
from fluidattacks_core.logging.sources.types import SourceStrategy
|
|
25
23
|
|
|
26
24
|
|
|
27
25
|
class ColorfulFormatter(logging.Formatter):
|
|
@@ -83,8 +81,7 @@ class CustomJsonFormatter(JsonFormatter):
|
|
|
83
81
|
log_record["file_location"] = f"{record.filename}:{record.lineno}"
|
|
84
82
|
log_record["lineno"] = record.lineno
|
|
85
83
|
|
|
86
|
-
self.
|
|
87
|
-
self._add_deployment_fields(log_record)
|
|
84
|
+
self._add_source_fields(log_record)
|
|
88
85
|
|
|
89
86
|
self._add_default_telemetry_fields(log_record)
|
|
90
87
|
self._add_error_fields(log_record, record)
|
|
@@ -99,41 +96,27 @@ class CustomJsonFormatter(JsonFormatter):
|
|
|
99
96
|
return int(timestamp)
|
|
100
97
|
return round(datetime.now(tz=UTC).timestamp() * 1000)
|
|
101
98
|
|
|
102
|
-
def
|
|
103
|
-
"""Add
|
|
99
|
+
def _add_source_fields(self, log_record: dict[str, Any]) -> None:
|
|
100
|
+
"""Add source information to the log record.
|
|
104
101
|
|
|
105
102
|
It includes:
|
|
106
103
|
- Source
|
|
107
104
|
- Service
|
|
108
105
|
- Version
|
|
106
|
+
- Deployment environment
|
|
107
|
+
- Other metadata according to the source
|
|
109
108
|
"""
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
def _add_deployment_fields(self, log_record: dict[str, Any]) -> None:
|
|
122
|
-
"""Add deployment information to the log record.
|
|
123
|
-
|
|
124
|
-
It includes:
|
|
125
|
-
- Environment
|
|
126
|
-
- Pipeline where the log was generated
|
|
127
|
-
"""
|
|
128
|
-
env = get_environment_metadata().environment
|
|
129
|
-
|
|
130
|
-
log_record["dd.environment"] = env
|
|
131
|
-
log_record["deployment.environment"] = env
|
|
132
|
-
|
|
133
|
-
if pipeline := get_pipeline_environment():
|
|
134
|
-
log_record["deployment.pipeline.type"] = pipeline.upper()
|
|
135
|
-
for key, value in get_pipeline_metadata(pipeline).items():
|
|
136
|
-
log_record[f"deployment.pipeline.{key}"] = value
|
|
109
|
+
supported_sources: list[type[SourceStrategy]] = [
|
|
110
|
+
LambdaSource,
|
|
111
|
+
BatchSource,
|
|
112
|
+
PipelineSource,
|
|
113
|
+
ContainerSource,
|
|
114
|
+
DefaultSource,
|
|
115
|
+
]
|
|
116
|
+
for source in supported_sources:
|
|
117
|
+
if source.detect():
|
|
118
|
+
log_record.update(source.log_metadata())
|
|
119
|
+
break
|
|
137
120
|
|
|
138
121
|
def _add_default_telemetry_fields(self, log_record: dict[str, Any]) -> None:
|
|
139
122
|
"""Add default metadata fields to the log record.
|
|
@@ -18,8 +18,8 @@ class DebuggingHandler(logging.StreamHandler[TextIO]):
|
|
|
18
18
|
- Formatter: `ColorfulFormatter`
|
|
19
19
|
"""
|
|
20
20
|
|
|
21
|
-
def __init__(self) -> None:
|
|
22
|
-
super().__init__(
|
|
21
|
+
def __init__(self, stream: TextIO = sys.stderr) -> None:
|
|
22
|
+
super().__init__(stream)
|
|
23
23
|
self.addFilter(NoProductionFilter())
|
|
24
24
|
self.setFormatter(ColorfulFormatter())
|
|
25
25
|
|
|
@@ -32,8 +32,8 @@ class ProductionSyncHandler(logging.StreamHandler[TextIO]):
|
|
|
32
32
|
- Formatter: `CustomJsonFormatter`
|
|
33
33
|
"""
|
|
34
34
|
|
|
35
|
-
def __init__(self) -> None:
|
|
36
|
-
super().__init__(
|
|
35
|
+
def __init__(self, stream: TextIO = sys.stderr) -> None:
|
|
36
|
+
super().__init__(stream)
|
|
37
37
|
self.addFilter(ProductionOnlyFilter())
|
|
38
38
|
self.setFormatter(CustomJsonFormatter())
|
|
39
39
|
|
|
@@ -46,8 +46,8 @@ class ProductionAsyncHandler(QueueHandler):
|
|
|
46
46
|
- Formatter: `CustomJsonFormatter`
|
|
47
47
|
"""
|
|
48
48
|
|
|
49
|
-
def __init__(self) -> None:
|
|
50
|
-
handler = logging.StreamHandler(
|
|
49
|
+
def __init__(self, stream: TextIO = sys.stderr) -> None:
|
|
50
|
+
handler = logging.StreamHandler(stream)
|
|
51
51
|
handler.addFilter(ProductionOnlyFilter())
|
|
52
52
|
handler.setFormatter(CustomJsonFormatter())
|
|
53
53
|
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
if TYPE_CHECKING:
|
|
6
|
+
from logging.config import _DictConfigArgs
|
|
7
|
+
|
|
8
|
+
# Main formats
|
|
9
|
+
DATE_FORMAT = "%Y-%m-%dT%H:%M:%S%z"
|
|
10
|
+
"""
|
|
11
|
+
Default date format for logs.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
PRODUCT_LOGGING: _DictConfigArgs = {
|
|
16
|
+
"version": 1,
|
|
17
|
+
"disable_existing_loggers": False,
|
|
18
|
+
"handlers": {
|
|
19
|
+
"production_handler": {
|
|
20
|
+
"class": "fluidattacks_core.logging.handlers.ProductionSyncHandler",
|
|
21
|
+
"stream": "ext://sys.stderr",
|
|
22
|
+
},
|
|
23
|
+
"debugging_handler": {
|
|
24
|
+
"class": "fluidattacks_core.logging.handlers.DebuggingHandler",
|
|
25
|
+
"stream": "ext://sys.stderr",
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
"root": {
|
|
29
|
+
"handlers": ["production_handler", "debugging_handler"],
|
|
30
|
+
"level": "INFO",
|
|
31
|
+
},
|
|
32
|
+
}
|
|
33
|
+
"""
|
|
34
|
+
Default logging configuration dict for all the products.
|
|
35
|
+
|
|
36
|
+
Required environment variables:
|
|
37
|
+
- `PRODUCT_ID`
|
|
38
|
+
- `CI_COMMIT_REF_NAME`
|
|
39
|
+
- `CI_COMMIT_SHA`
|
|
40
|
+
"""
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
from fluidattacks_core.logging.sources.types import SourceStrategy
|
|
2
|
+
from fluidattacks_core.logging.sources.utils import get_env_var, get_environment, get_version
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class DefaultSource(SourceStrategy):
|
|
6
|
+
@staticmethod
|
|
7
|
+
def detect() -> bool:
|
|
8
|
+
return True
|
|
9
|
+
|
|
10
|
+
@staticmethod
|
|
11
|
+
def log_metadata() -> dict[str, str]:
|
|
12
|
+
return {
|
|
13
|
+
"ddsource": "python",
|
|
14
|
+
"dd.service": get_env_var("PRODUCT_ID") or "unknown",
|
|
15
|
+
"dd.version": get_version(),
|
|
16
|
+
"deployment.environment": get_environment(),
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class LambdaSource(SourceStrategy):
|
|
21
|
+
@staticmethod
|
|
22
|
+
def detect() -> bool:
|
|
23
|
+
return get_env_var("AWS_LAMBDA_FUNCTION_NAME") is not None
|
|
24
|
+
|
|
25
|
+
@staticmethod
|
|
26
|
+
def log_metadata() -> dict[str, str]:
|
|
27
|
+
return {
|
|
28
|
+
"ddsource": "lambda",
|
|
29
|
+
"dd.service": get_env_var("AWS_LAMBDA_FUNCTION_NAME") or "unknown",
|
|
30
|
+
"dd.version": get_version(),
|
|
31
|
+
"deployment.environment": get_environment(),
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class BatchSource(SourceStrategy):
|
|
36
|
+
@staticmethod
|
|
37
|
+
def detect() -> bool:
|
|
38
|
+
return get_env_var("AWS_BATCH_JOB_ID") is not None
|
|
39
|
+
|
|
40
|
+
@staticmethod
|
|
41
|
+
def log_metadata() -> dict[str, str]:
|
|
42
|
+
job_name = get_env_var("JOB_DEFINITION_NAME")
|
|
43
|
+
job_queue = get_env_var("AWS_BATCH_JQ_NAME")
|
|
44
|
+
product_id = get_env_var("PRODUCT_ID")
|
|
45
|
+
service = job_name or (f"from-{job_queue}" if job_queue else product_id)
|
|
46
|
+
return {
|
|
47
|
+
"ddsource": "batch",
|
|
48
|
+
"dd.service": service or "unknown",
|
|
49
|
+
"dd.version": get_version(),
|
|
50
|
+
"deployment.environment": get_environment(),
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class PipelineSource(SourceStrategy):
|
|
55
|
+
@staticmethod
|
|
56
|
+
def detect() -> bool:
|
|
57
|
+
return PipelineSource._get_pipeline_metadata() is not None
|
|
58
|
+
|
|
59
|
+
@staticmethod
|
|
60
|
+
def log_metadata() -> dict[str, str]:
|
|
61
|
+
metadata = PipelineSource._get_pipeline_metadata()
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
"ddsource": "ci",
|
|
65
|
+
"dd.service": get_env_var("PRODUCT_ID") or "unknown",
|
|
66
|
+
"dd.version": get_version(),
|
|
67
|
+
"deployment.environment": get_environment(),
|
|
68
|
+
**(metadata or {}),
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
@staticmethod
|
|
72
|
+
def _get_pipeline_metadata() -> dict[str, str] | None:
|
|
73
|
+
pipeline = None
|
|
74
|
+
if get_env_var("CI_JOB_ID"):
|
|
75
|
+
pipeline = "gitlab_ci"
|
|
76
|
+
elif get_env_var("CIRCLECI"):
|
|
77
|
+
pipeline = "circleci"
|
|
78
|
+
elif get_env_var("System.JobId"):
|
|
79
|
+
pipeline = "azure_devops"
|
|
80
|
+
elif get_env_var("BUILD_NUMBER"):
|
|
81
|
+
pipeline = "jenkins"
|
|
82
|
+
|
|
83
|
+
if pipeline is None:
|
|
84
|
+
return None
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
"ddsource": f"ci/{pipeline}",
|
|
88
|
+
"deployment.pipeline.type": pipeline,
|
|
89
|
+
**(
|
|
90
|
+
{
|
|
91
|
+
"deployment.pipeline.CI_JOB_ID": get_env_var("CI_JOB_ID") or "unknown",
|
|
92
|
+
"deployment.pipeline.CI_JOB_URL": get_env_var("CI_JOB_URL") or "unknown",
|
|
93
|
+
}
|
|
94
|
+
if pipeline == "gitlab_ci"
|
|
95
|
+
else {}
|
|
96
|
+
),
|
|
97
|
+
**(
|
|
98
|
+
{
|
|
99
|
+
"deployment.pipeline.CIRCLE_BUILD_NUM": get_env_var("CIRCLE_BUILD_NUM")
|
|
100
|
+
or "unknown",
|
|
101
|
+
"deployment.pipeline.CIRCLE_BUILD_URL": get_env_var("CIRCLE_BUILD_URL")
|
|
102
|
+
or "unknown",
|
|
103
|
+
}
|
|
104
|
+
if pipeline == "circleci"
|
|
105
|
+
else {}
|
|
106
|
+
),
|
|
107
|
+
**(
|
|
108
|
+
{
|
|
109
|
+
"deployment.pipeline.System.JobId": get_env_var("System.JobId") or "unknown",
|
|
110
|
+
}
|
|
111
|
+
if pipeline == "azure_devops"
|
|
112
|
+
else {}
|
|
113
|
+
),
|
|
114
|
+
**(
|
|
115
|
+
{
|
|
116
|
+
"deployment.pipeline.BUILD_NUMBER": get_env_var("BUILD_NUMBER") or "unknown",
|
|
117
|
+
"deployment.pipeline.BUILD_ID": get_env_var("BUILD_ID") or "unknown",
|
|
118
|
+
"deployment.pipeline.BUILD_URL": get_env_var("BUILD_URL") or "unknown",
|
|
119
|
+
}
|
|
120
|
+
if pipeline == "jenkins"
|
|
121
|
+
else {}
|
|
122
|
+
),
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
class ContainerSource(SourceStrategy):
|
|
127
|
+
@staticmethod
|
|
128
|
+
def detect() -> bool:
|
|
129
|
+
return get_env_var("CONTAINER_IMAGE") is not None
|
|
130
|
+
|
|
131
|
+
@staticmethod
|
|
132
|
+
def log_metadata() -> dict[str, str]:
|
|
133
|
+
return {
|
|
134
|
+
"ddsource": "container",
|
|
135
|
+
"dd.service": get_env_var("PRODUCT_ID") or "unknown",
|
|
136
|
+
"dd.version": get_version(),
|
|
137
|
+
"deployment.environment": get_environment(),
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
__all__ = [
|
|
142
|
+
"BatchSource",
|
|
143
|
+
"ContainerSource",
|
|
144
|
+
"DefaultSource",
|
|
145
|
+
"LambdaSource",
|
|
146
|
+
"PipelineSource",
|
|
147
|
+
]
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class SourceStrategy(ABC):
|
|
5
|
+
@staticmethod
|
|
6
|
+
@abstractmethod
|
|
7
|
+
def detect() -> bool:
|
|
8
|
+
"""Detect if the current runtime is using this source."""
|
|
9
|
+
|
|
10
|
+
@staticmethod
|
|
11
|
+
@abstractmethod
|
|
12
|
+
def log_metadata() -> dict[str, str]:
|
|
13
|
+
"""Get the metadata to be added to the log record."""
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from typing import Literal
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def get_env_var(key: str) -> str | None:
|
|
6
|
+
return os.environ.get(key)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def get_environment() -> Literal["development", "production"]:
|
|
10
|
+
branch = os.environ.get("CI_COMMIT_REF_NAME", "default")
|
|
11
|
+
return "production" if branch == "trunk" else "development"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def get_version() -> str:
|
|
15
|
+
return os.environ.get("CI_COMMIT_SHA", "00000000")[:8]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def set_product_id(product_id: str) -> None:
|
|
19
|
+
os.environ["PRODUCT_ID"] = product_id
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def set_commit_sha(commit_sha: str) -> None:
|
|
23
|
+
os.environ["CI_COMMIT_SHA"] = commit_sha
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def set_commit_ref_name(commit_ref_name: str) -> None:
|
|
27
|
+
os.environ["CI_COMMIT_REF_NAME"] = commit_ref_name
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
DEFAULT_TELEMETRY_METADATA: dict[str, Any] = {}
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def set_telemetry_metadata(config: dict[str, Any]) -> None:
|
|
8
|
+
DEFAULT_TELEMETRY_METADATA.update(config)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def get_telemetry_metadata() -> dict[str, Any]:
|
|
12
|
+
return DEFAULT_TELEMETRY_METADATA
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def debug_logs() -> None:
|
|
16
|
+
"""Test all the log levels in the root logger and a custom logger."""
|
|
17
|
+
root_logger = logging.getLogger()
|
|
18
|
+
|
|
19
|
+
root_logger.debug("This is a debug log")
|
|
20
|
+
root_logger.info("This is an info log")
|
|
21
|
+
root_logger.warning("This is a warning log")
|
|
22
|
+
root_logger.error("This is an error log")
|
|
23
|
+
root_logger.critical("This is a critical log")
|
|
24
|
+
|
|
25
|
+
logger = logging.getLogger("test-logger")
|
|
26
|
+
logger.debug("This is a debug log")
|
|
27
|
+
logger.info("This is an info log")
|
|
28
|
+
logger.warning("This is a warning log")
|
|
29
|
+
logger.error("This is an error log")
|
|
30
|
+
logger.critical("This is a critical log")
|
|
31
|
+
|
|
32
|
+
try:
|
|
33
|
+
msg = "Missing key"
|
|
34
|
+
raise KeyError(msg) # noqa: TRY301
|
|
35
|
+
except KeyError as e:
|
|
36
|
+
root_logger.exception(e) # noqa:TRY401
|
|
37
|
+
logger.exception(e) # noqa:TRY401
|
|
@@ -43,8 +43,10 @@ fluidattacks_core/logging/filters.py
|
|
|
43
43
|
fluidattacks_core/logging/formatters.py
|
|
44
44
|
fluidattacks_core/logging/handlers.py
|
|
45
45
|
fluidattacks_core/logging/presets.py
|
|
46
|
-
fluidattacks_core/logging/types.py
|
|
47
46
|
fluidattacks_core/logging/utils.py
|
|
47
|
+
fluidattacks_core/logging/sources/__init__.py
|
|
48
|
+
fluidattacks_core/logging/sources/types.py
|
|
49
|
+
fluidattacks_core/logging/sources/utils.py
|
|
48
50
|
fluidattacks_core/sarif/__init__.py
|
|
49
51
|
fluidattacks_core/semver/__init__.py
|
|
50
52
|
fluidattacks_core/semver/match_versions.py
|