Skip to content

Commit b4c9899

Browse files
dploegerDennis Ploeger
authored andcommitted
feat: Make logging of dumps optional to work around max_line_size problem
Includes better documentation and parameter support Includes test suite
1 parent b0ddf6c commit b4c9899

File tree

6 files changed

+296
-96
lines changed

6 files changed

+296
-96
lines changed

.github/workflows/test.yml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# This workflow will install Python dependencies, run tests and lint with a single version of Python
2+
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python
3+
4+
name: Python application
5+
6+
on:
7+
push:
8+
branches: [ "main" ]
9+
pull_request:
10+
branches: [ "main" ]
11+
12+
permissions:
13+
contents: read
14+
15+
jobs:
16+
build:
17+
18+
runs-on: ubuntu-latest
19+
20+
steps:
21+
- uses: actions/checkout@v4
22+
- name: Set up Python 3.10
23+
uses: actions/setup-python@v3
24+
with:
25+
python-version: "3.10"
26+
- name: Install dependencies
27+
run: |
28+
python -m pip install --upgrade pip
29+
pip install -r requirements.txt -r requirements-dev.txt
30+
- name: Run test
31+
run: |
32+
cd test
33+
python -m ansible.cli.playbook playbook.yaml -i default/inventory.yaml -e expected_records=20 -vvv
34+
LOKI_ENABLED_DUMPS=play python -m ansible.cli.playbook playbook.yaml -i default/inventory.yaml -e expected_records=26 -vvv

loki.py

Lines changed: 151 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,59 @@
1818
requirements:
1919
- set as loki in configuration
2020
options:
21+
loki_url:
22+
description: URL to access loki gateway
23+
required: True
24+
env:
25+
- name: LOKI_URL
26+
ini:
27+
- section: loki
28+
key: url
29+
loki_username:
30+
description: Username to access loki gateway
31+
env:
32+
- name: LOKI_USERNAME
33+
ini:
34+
- section: loki
35+
key: username
36+
loki_password:
37+
description: Password to access loki gateway
38+
env:
39+
- name: LOKI_PASSWORD
40+
ini:
41+
- section: loki
42+
key: password
43+
loki_org_id:
44+
description: Organization to use as a tenant when connecting to loki
45+
env:
46+
- name: LOKI_ORG_ID
47+
ini:
48+
- section: loki
49+
key: org_id
50+
loki_default_tags:
51+
description: "Tags (key:value) to set for each log line"
52+
env:
53+
- name: LOKI_DEFAULT_TAGS
54+
ini:
55+
- section: loki
56+
key: default_tags
57+
type: list
2158
result_format:
22-
name: Result format
2359
default: json
2460
description: Format used in results (will be set to json)
2561
pretty_results:
26-
name: Print results pretty
2762
default: False
2863
description: Whether to print results pretty (will be set to false)
64+
enabled_dumps:
65+
description: |
66+
Dumps to enable. The values playbook, diff, play, task and runner are available.
67+
This usually requires that max_line_size in Loki is set to a higher value than 256kb.
68+
type: list
69+
env:
70+
- name: LOKI_ENABLED_DUMPS
71+
ini:
72+
- section: loki
73+
key: enabled_dumps
2974
'''
3075

3176

@@ -37,28 +82,26 @@ class CallbackModule(CallbackBase):
3782
CALLBACK_NAME = 'loki'
3883
ALL_METRICS = ["changed", "custom", "dark", "failures", "ignored", "ok", "processed", "rescued", "skipped"]
3984

40-
def __init__(self):
41-
super().__init__()
42-
43-
if "LOKI_URL" not in os.environ:
44-
raise "LOKI_URL environment variable not specified."
45-
85+
def __init__(self, display=None, options=None):
86+
super().__init__(display, options)
87+
self.set_options()
4688
auth = ()
47-
if "LOKI_USERNAME" in os.environ and "LOKI_PASSWORD" in os.environ:
48-
auth = (os.environ["LOKI_USERNAME"], os.environ["LOKI_PASSWORD"])
89+
90+
if self.get_option("loki_username") and self.get_option("loki_password"):
91+
auth = (self.get_option("loki_username"), self.get_option("loki_password"))
4992

5093
headers = {}
51-
if "LOKI_ORG_ID" in os.environ:
52-
headers["X-Scope-OrgID"] = os.environ["LOKI_ORG_ID"]
94+
if self.get_option("loki_org_id"):
95+
headers["X-Scope-OrgID"] = self.get_option("loki_org_id")
5396

5497
tags = {}
55-
if "LOKI_DEFAULT_TAGS" in os.environ:
56-
for tagvalue in os.environ["LOKI_DEFAULT_TAGS"].split(","):
98+
if self.get_option("loki_default_tags"):
99+
for tagvalue in self.get_option("loki_default_tags"):
57100
(tag, value) = tagvalue.split(":")
58101
tags[tag] = value
59102

60103
handler = logging_loki.LokiHandler(
61-
url=os.environ["LOKI_URL"],
104+
url=self.get_option("loki_url"),
62105
tags=tags,
63106
auth=auth,
64107
headers=headers,
@@ -77,35 +120,41 @@ def __init__(self):
77120
self.set_option("result_format", "json")
78121
self.set_option("pretty_results", False)
79122

123+
def _dump_enabled(self, dump):
124+
return self.get_option("enabled_dumps") and dump in self.get_option("enabled_dumps")
125+
126+
80127
def v2_playbook_on_start(self, playbook):
81128
self.playbook = os.path.join(playbook._basedir, playbook._file_name)
82129
self.run_timestamp = datetime.datetime.now().isoformat()
83130
self.logger.info(
84131
"Starting playbook %s" % self.playbook,
85132
extra={"tags": {"playbook": self.playbook, "run_timestamp": self.run_timestamp}}
86133
)
87-
self.logger.debug(
88-
jsonpickle.encode(playbook.__dict__),
89-
extra={"tags": {"playbook": self.playbook, "run_timestamp": self.run_timestamp, "dump": "playbook"}}
90-
)
134+
if self._dump_enabled("playbook"):
135+
self.logger.debug(
136+
jsonpickle.encode(playbook.__dict__),
137+
extra={"tags": {"playbook": self.playbook, "run_timestamp": self.run_timestamp, "dump": "playbook"}}
138+
)
91139

92140
def v2_playbook_on_play_start(self, play):
93141
self.current_play = play.name
94142
self.logger.info(
95143
"Starting play %s" % play.name,
96144
extra={"tags": {"playbook": self.playbook, "run_timestamp": self.run_timestamp, "play": self.current_play}}
97145
)
98-
self.logger.debug(
99-
jsonpickle.encode(play.__dict__),
100-
extra={
101-
"tags": {
102-
"playbook": self.playbook,
103-
"run_timestamp": self.run_timestamp,
104-
"play": self.current_play,
105-
"dump": "play"
146+
if self._dump_enabled("play"):
147+
self.logger.debug(
148+
jsonpickle.encode(play.__dict__),
149+
extra={
150+
"tags": {
151+
"playbook": self.playbook,
152+
"run_timestamp": self.run_timestamp,
153+
"play": self.current_play,
154+
"dump": "play"
155+
}
106156
}
107-
}
108-
)
157+
)
109158

110159
def v2_playbook_on_task_start(self, task, is_conditional):
111160
self.current_task = task.name
@@ -120,18 +169,19 @@ def v2_playbook_on_task_start(self, task, is_conditional):
120169
}
121170
}
122171
)
123-
self.logger.debug(
124-
jsonpickle.encode(task.__dict__),
125-
extra={
126-
"tags": {
127-
"playbook": self.playbook,
128-
"run_timestamp": self.run_timestamp,
129-
"play": self.current_play,
130-
"task": self.current_task,
131-
"dump": "task"
172+
if self._dump_enabled("task"):
173+
self.logger.debug(
174+
jsonpickle.encode(task.__dict__),
175+
extra={
176+
"tags": {
177+
"playbook": self.playbook,
178+
"run_timestamp": self.run_timestamp,
179+
"play": self.current_play,
180+
"task": self.current_task,
181+
"dump": "task"
182+
}
132183
}
133-
}
134-
)
184+
)
135185

136186
def v2_runner_on_ok(self, result):
137187
self.logger.debug(
@@ -145,18 +195,19 @@ def v2_runner_on_ok(self, result):
145195
}
146196
}
147197
)
148-
self.logger.debug(
149-
self._dump_results(result._result),
150-
extra={
151-
"tags": {
152-
"playbook": self.playbook,
153-
"run_timestamp": self.run_timestamp,
154-
"play": self.current_play,
155-
"task": self.current_task,
156-
"dump": "runner"
198+
if self._dump_enabled("runner"):
199+
self.logger.debug(
200+
self._dump_results(result._result),
201+
extra={
202+
"tags": {
203+
"playbook": self.playbook,
204+
"run_timestamp": self.run_timestamp,
205+
"play": self.current_play,
206+
"task": self.current_task,
207+
"dump": "runner"
208+
}
157209
}
158-
}
159-
)
210+
)
160211

161212
def v2_runner_on_failed(self, result, ignore_errors=False):
162213
level = logging.WARNING if ignore_errors else logging.ERROR
@@ -176,18 +227,19 @@ def v2_runner_on_failed(self, result, ignore_errors=False):
176227
}
177228
}
178229
)
179-
self.logger.debug(
180-
self._dump_results(result._result),
181-
extra={
182-
"tags": {
183-
"playbook": self.playbook,
184-
"run_timestamp": self.run_timestamp,
185-
"play": self.current_play,
186-
"task": self.current_task,
187-
"dump": "runner"
230+
if self._dump_enabled("runner"):
231+
self.logger.debug(
232+
self._dump_results(result._result),
233+
extra={
234+
"tags": {
235+
"playbook": self.playbook,
236+
"run_timestamp": self.run_timestamp,
237+
"play": self.current_play,
238+
"task": self.current_task,
239+
"dump": "runner"
240+
}
188241
}
189-
}
190-
)
242+
)
191243

192244
def v2_runner_on_skipped(self, result):
193245
self.logger.info(
@@ -201,18 +253,19 @@ def v2_runner_on_skipped(self, result):
201253
}
202254
}
203255
)
204-
self.logger.debug(
205-
self._dump_results(result._result),
206-
extra={
207-
"tags": {
208-
"playbook": self.playbook,
209-
"run_timestamp": self.run_timestamp,
210-
"play": self.current_play,
211-
"task": self.current_task,
212-
"dump": "runner"
256+
if self._dump_enabled("runner"):
257+
self.logger.debug(
258+
self._dump_results(result._result),
259+
extra={
260+
"tags": {
261+
"playbook": self.playbook,
262+
"run_timestamp": self.run_timestamp,
263+
"play": self.current_play,
264+
"task": self.current_task,
265+
"dump": "runner"
266+
}
213267
}
214-
}
215-
)
268+
)
216269

217270
def runner_on_unreachable(self, host, result):
218271
self.logger.error(
@@ -226,18 +279,19 @@ def runner_on_unreachable(self, host, result):
226279
}
227280
}
228281
)
229-
self.logger.debug(
230-
self._dump_results(result),
231-
extra={
232-
"tags": {
233-
"playbook": self.playbook,
234-
"run_timestamp": self.run_timestamp,
235-
"play": self.current_play,
236-
"task": self.current_task,
237-
"dump": "runner"
282+
if self._dump_enabled("runner"):
283+
self.logger.debug(
284+
self._dump_results(result),
285+
extra={
286+
"tags": {
287+
"playbook": self.playbook,
288+
"run_timestamp": self.run_timestamp,
289+
"play": self.current_play,
290+
"task": self.current_task,
291+
"dump": "runner"
292+
}
238293
}
239-
}
240-
)
294+
)
241295

242296
def v2_playbook_on_no_hosts_matched(self):
243297
self.logger.error(
@@ -263,19 +317,20 @@ def v2_on_file_diff(self, result):
263317
}
264318
}
265319
)
266-
for diff in diff_list:
267-
self.logger.debug(
268-
self._serialize_diff(diff),
269-
extra={
270-
"tags": {
271-
"playbook": self.playbook,
272-
"run_timestamp": self.run_timestamp,
273-
"play": self.current_play,
274-
"task": self.current_task,
275-
"dump": "diff"
320+
if self._dump_enabled("diff"):
321+
for diff in diff_list:
322+
self.logger.debug(
323+
self._serialize_diff(diff),
324+
extra={
325+
"tags": {
326+
"playbook": self.playbook,
327+
"run_timestamp": self.run_timestamp,
328+
"play": self.current_play,
329+
"task": self.current_task,
330+
"dump": "diff"
331+
}
276332
}
277-
}
278-
)
333+
)
279334

280335
def v2_playbook_on_stats(self, stats):
281336
summarize_metrics = {}

test/ansible.cfg

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[defaults]
2+
callback_plugins=../
3+
4+
[loki]
5+
url=http://localhost:3100/loki/api/v1/push
6+
default_tags=run:test

test/default/inventory.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
unreachable:
2+
hosts:
3+
unreachable_host:
4+
ansible_host: 1.1.1.1

0 commit comments

Comments
 (0)