loggerbuf 0.9.2__tar.gz → 0.9.3__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.
Files changed (45) hide show
  1. {loggerbuf-0.9.2 → loggerbuf-0.9.3}/PKG-INFO +57 -11
  2. {loggerbuf-0.9.2 → loggerbuf-0.9.3}/README.md +56 -10
  3. {loggerbuf-0.9.2 → loggerbuf-0.9.3}/pyproject.toml +1 -1
  4. {loggerbuf-0.9.2 → loggerbuf-0.9.3}/src/loggerbuf/data_logs/main_data_pb2.py +13 -9
  5. {loggerbuf-0.9.2 → loggerbuf-0.9.3}/src/loggerbuf/data_logs/registry_pb2.py +11 -10
  6. {loggerbuf-0.9.2 → loggerbuf-0.9.3}/src/loggerbuf/telemetry.py +101 -16
  7. {loggerbuf-0.9.2 → loggerbuf-0.9.3}/src/loggerbuf.egg-info/PKG-INFO +57 -11
  8. {loggerbuf-0.9.2 → loggerbuf-0.9.3}/tests/test_telemetry.py +34 -0
  9. {loggerbuf-0.9.2 → loggerbuf-0.9.3}/setup.cfg +0 -0
  10. {loggerbuf-0.9.2 → loggerbuf-0.9.3}/src/loggerbuf/__init__.py +0 -0
  11. {loggerbuf-0.9.2 → loggerbuf-0.9.3}/src/loggerbuf/cli/__init__.py +0 -0
  12. {loggerbuf-0.9.2 → loggerbuf-0.9.3}/src/loggerbuf/cli/console.py +0 -0
  13. {loggerbuf-0.9.2 → loggerbuf-0.9.3}/src/loggerbuf/cli/handlers/__init__.py +0 -0
  14. {loggerbuf-0.9.2 → loggerbuf-0.9.3}/src/loggerbuf/cli/handlers/decode.py +0 -0
  15. {loggerbuf-0.9.2 → loggerbuf-0.9.3}/src/loggerbuf/cli/handlers/events.py +0 -0
  16. {loggerbuf-0.9.2 → loggerbuf-0.9.3}/src/loggerbuf/cli/handlers/fields.py +0 -0
  17. {loggerbuf-0.9.2 → loggerbuf-0.9.3}/src/loggerbuf/cli/handlers/protos.py +0 -0
  18. {loggerbuf-0.9.2 → loggerbuf-0.9.3}/src/loggerbuf/cli/handlers/stress.py +0 -0
  19. {loggerbuf-0.9.2 → loggerbuf-0.9.3}/src/loggerbuf/cli/utils/__init__.py +0 -0
  20. {loggerbuf-0.9.2 → loggerbuf-0.9.3}/src/loggerbuf/cli/utils/registry.py +0 -0
  21. {loggerbuf-0.9.2 → loggerbuf-0.9.3}/src/loggerbuf/cli/utils/schema_validator.py +0 -0
  22. {loggerbuf-0.9.2 → loggerbuf-0.9.3}/src/loggerbuf/config.py +0 -0
  23. {loggerbuf-0.9.2 → loggerbuf-0.9.3}/src/loggerbuf/data_logs/__init__.py +0 -0
  24. {loggerbuf-0.9.2 → loggerbuf-0.9.3}/src/loggerbuf/debugger.py +0 -0
  25. {loggerbuf-0.9.2 → loggerbuf-0.9.3}/src/loggerbuf/queue_metrics.py +0 -0
  26. {loggerbuf-0.9.2 → loggerbuf-0.9.3}/src/loggerbuf/schema_loader.py +0 -0
  27. {loggerbuf-0.9.2 → loggerbuf-0.9.3}/src/loggerbuf.egg-info/SOURCES.txt +0 -0
  28. {loggerbuf-0.9.2 → loggerbuf-0.9.3}/src/loggerbuf.egg-info/dependency_links.txt +0 -0
  29. {loggerbuf-0.9.2 → loggerbuf-0.9.3}/src/loggerbuf.egg-info/entry_points.txt +0 -0
  30. {loggerbuf-0.9.2 → loggerbuf-0.9.3}/src/loggerbuf.egg-info/requires.txt +0 -0
  31. {loggerbuf-0.9.2 → loggerbuf-0.9.3}/src/loggerbuf.egg-info/top_level.txt +0 -0
  32. {loggerbuf-0.9.2 → loggerbuf-0.9.3}/tests/test_cli_decode_debug.py +0 -0
  33. {loggerbuf-0.9.2 → loggerbuf-0.9.3}/tests/test_config.py +0 -0
  34. {loggerbuf-0.9.2 → loggerbuf-0.9.3}/tests/test_console_cli.py +0 -0
  35. {loggerbuf-0.9.2 → loggerbuf-0.9.3}/tests/test_create_classes.py +0 -0
  36. {loggerbuf-0.9.2 → loggerbuf-0.9.3}/tests/test_debugger.py +0 -0
  37. {loggerbuf-0.9.2 → loggerbuf-0.9.3}/tests/test_debugger_coverage.py +0 -0
  38. {loggerbuf-0.9.2 → loggerbuf-0.9.3}/tests/test_decoder.py +0 -0
  39. {loggerbuf-0.9.2 → loggerbuf-0.9.3}/tests/test_events_cli.py +0 -0
  40. {loggerbuf-0.9.2 → loggerbuf-0.9.3}/tests/test_json_formatter.py +0 -0
  41. {loggerbuf-0.9.2 → loggerbuf-0.9.3}/tests/test_protos_coverage.py +0 -0
  42. {loggerbuf-0.9.2 → loggerbuf-0.9.3}/tests/test_queue_metrics.py +0 -0
  43. {loggerbuf-0.9.2 → loggerbuf-0.9.3}/tests/test_schema_validator.py +0 -0
  44. {loggerbuf-0.9.2 → loggerbuf-0.9.3}/tests/test_telemetry_counters.py +0 -0
  45. {loggerbuf-0.9.2 → loggerbuf-0.9.3}/tests/test_telemetry_coverage.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: loggerbuf
3
- Version: 0.9.2
3
+ Version: 0.9.3
4
4
  Summary: A powerful structured telemetry and debugging logger with Protobuf schemas
5
5
  Author-email: LoggerBuf Team <hello@loggerbuf.dev>
6
6
  Project-URL: Repository, https://github.com/calavraian/LoggerBuf
@@ -28,6 +28,8 @@ Requires-Dist: psutil
28
28
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
29
29
  [![Build Status](https://github.com/calavraian/LoggerBuf/actions/workflows/ci.yml/badge.svg)](https://github.com/calavraian/LoggerBuf/actions)
30
30
 
31
+ **[Visit the official website: loggerbuf.dev](https://loggerbuf.dev)**
32
+
31
33
  </div>
32
34
 
33
35
  🇺🇸 English | 🇪🇸 [Español](README.es.md)
@@ -64,6 +66,15 @@ You are now ready to log! You can also run these sub-commands individually if yo
64
66
 
65
67
  ---
66
68
 
69
+ ## 🤖 AI Agent Integration (Agent Skill)
70
+
71
+ LoggerBuf is incredibly easy for AI coding assistants (like Gemini, Claude, or Cursor) to use!
72
+ We provide a dedicated **Agent Skill** (`SKILL.md`) located in `.agents/skills/loggerbuf/SKILL.md` within this repository.
73
+
74
+ If you are using an agentic AI system that supports custom skills, simply point it to our `SKILL.md`. Your agent will instantly understand the CLI workflow, Schema Management rules, Event formatting guidelines, and DRY Python implementation patterns required to integrate LoggerBuf seamlessly into your project.
75
+
76
+ ---
77
+
67
78
  ## 🤔 Why LoggerBuf? (The Problem & The Solution)
68
79
 
69
80
  **The Problem:**
@@ -169,22 +180,57 @@ The custom formatter instantly reveals the *where* and *what* of any log entry w
169
180
  - `message::> [Payload]`: The actual log message or serialized JSON object.
170
181
 
171
182
  ### 2. Analytical Event Logging (Telemetry)
172
- Telemetry uses Protobuf. Every event you track should be categorized using your custom `EventType` and `EventStatus` enums to ensure consistency across your organization. Just like the debugger, Telemetry automatically injects creation timestamps and routing headers behind the scenes.
183
+ Telemetry uses Protobuf to log structured events. Every event is categorized using `EventType` and `EventStatus`. LoggerBuf automatically injects timestamps, file names, lines, and system identifiers.
184
+
185
+ **Way 1: Standard Method (`log_event`)**
186
+ The simplest way to log an event. You can pass any field defined in your `main_data.proto` as a keyword argument (kwargs).
173
187
 
174
188
  ```python
175
- from telemetry import TelemetryLog
176
- from data_logs import Event, EventType, EventStatus
189
+ from loggerbuf.telemetry import TelemetryLog
190
+
191
+ telemetry = TelemetryLog("MAIN")
192
+
193
+ # Fire an event asynchronously
194
+ telemetry.log_event(
195
+ event_type="EVENT_DATA_BASE_PROCESSING",
196
+ general_note="User uploaded a new invoice",
197
+ status="STATUS_COMPLETED",
198
+ duration_ms=150
199
+ )
200
+ ```
201
+
202
+ **Way 2: Context Manager (`event_context`)**
203
+ Ideal for tracking block execution. It automatically calculates `duration_ms` and will set the status to `STATUS_ERROR` if an unhandled exception occurs inside the block.
204
+
205
+ ```python
206
+ from loggerbuf.telemetry import TelemetryLog
207
+
208
+ telemetry = TelemetryLog("MAIN")
209
+
210
+ with telemetry.event_context(general_note="Processing database migration"):
211
+ # ... your code here ...
212
+ # duration_ms is automatically tracked and the event is fired when exiting the block
213
+ pass
214
+ ```
215
+
216
+ **Way 3: Manual Object Building (`send`)**
217
+ If you need absolute control, you can build the Protobuf object manually:
218
+
219
+ ```python
220
+ from loggerbuf.telemetry import TelemetryLog
221
+ from loggerbuf import schema_loader
222
+
223
+ main_data_pb2 = schema_loader.get_main_data_pb2()
224
+ registry_pb2 = schema_loader.get_registry_pb2()
177
225
 
178
- # 1. Initialize Logger
179
226
  telemetry = TelemetryLog("MAIN")
180
227
 
181
- # 2. Create and populate Event
182
- event = Event()
183
- event.event_type = EventType.EVENT_DATA_BASE_PROCESSING
184
- event.general_note = "User successfully registered"
185
- event.status = EventStatus.STATUS_COMPLETED
228
+ event = main_data_pb2.Event()
229
+ event.event_type = registry_pb2.EventType.EVENT_DATA_BASE_PROCESSING
230
+ event.general_note = "User uploaded a new invoice"
231
+ event.status = registry_pb2.EventStatus.STATUS_PENDING
186
232
 
187
- # Send it to the async binary queue
233
+ # Fire the event asynchronously
188
234
  telemetry.send(event)
189
235
  ```
190
236
 
@@ -7,6 +7,8 @@
7
7
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
8
8
  [![Build Status](https://github.com/calavraian/LoggerBuf/actions/workflows/ci.yml/badge.svg)](https://github.com/calavraian/LoggerBuf/actions)
9
9
 
10
+ **[Visit the official website: loggerbuf.dev](https://loggerbuf.dev)**
11
+
10
12
  </div>
11
13
 
12
14
  🇺🇸 English | 🇪🇸 [Español](README.es.md)
@@ -43,6 +45,15 @@ You are now ready to log! You can also run these sub-commands individually if yo
43
45
 
44
46
  ---
45
47
 
48
+ ## 🤖 AI Agent Integration (Agent Skill)
49
+
50
+ LoggerBuf is incredibly easy for AI coding assistants (like Gemini, Claude, or Cursor) to use!
51
+ We provide a dedicated **Agent Skill** (`SKILL.md`) located in `.agents/skills/loggerbuf/SKILL.md` within this repository.
52
+
53
+ If you are using an agentic AI system that supports custom skills, simply point it to our `SKILL.md`. Your agent will instantly understand the CLI workflow, Schema Management rules, Event formatting guidelines, and DRY Python implementation patterns required to integrate LoggerBuf seamlessly into your project.
54
+
55
+ ---
56
+
46
57
  ## 🤔 Why LoggerBuf? (The Problem & The Solution)
47
58
 
48
59
  **The Problem:**
@@ -148,22 +159,57 @@ The custom formatter instantly reveals the *where* and *what* of any log entry w
148
159
  - `message::> [Payload]`: The actual log message or serialized JSON object.
149
160
 
150
161
  ### 2. Analytical Event Logging (Telemetry)
151
- Telemetry uses Protobuf. Every event you track should be categorized using your custom `EventType` and `EventStatus` enums to ensure consistency across your organization. Just like the debugger, Telemetry automatically injects creation timestamps and routing headers behind the scenes.
162
+ Telemetry uses Protobuf to log structured events. Every event is categorized using `EventType` and `EventStatus`. LoggerBuf automatically injects timestamps, file names, lines, and system identifiers.
163
+
164
+ **Way 1: Standard Method (`log_event`)**
165
+ The simplest way to log an event. You can pass any field defined in your `main_data.proto` as a keyword argument (kwargs).
152
166
 
153
167
  ```python
154
- from telemetry import TelemetryLog
155
- from data_logs import Event, EventType, EventStatus
168
+ from loggerbuf.telemetry import TelemetryLog
169
+
170
+ telemetry = TelemetryLog("MAIN")
171
+
172
+ # Fire an event asynchronously
173
+ telemetry.log_event(
174
+ event_type="EVENT_DATA_BASE_PROCESSING",
175
+ general_note="User uploaded a new invoice",
176
+ status="STATUS_COMPLETED",
177
+ duration_ms=150
178
+ )
179
+ ```
180
+
181
+ **Way 2: Context Manager (`event_context`)**
182
+ Ideal for tracking block execution. It automatically calculates `duration_ms` and will set the status to `STATUS_ERROR` if an unhandled exception occurs inside the block.
183
+
184
+ ```python
185
+ from loggerbuf.telemetry import TelemetryLog
186
+
187
+ telemetry = TelemetryLog("MAIN")
188
+
189
+ with telemetry.event_context(general_note="Processing database migration"):
190
+ # ... your code here ...
191
+ # duration_ms is automatically tracked and the event is fired when exiting the block
192
+ pass
193
+ ```
194
+
195
+ **Way 3: Manual Object Building (`send`)**
196
+ If you need absolute control, you can build the Protobuf object manually:
197
+
198
+ ```python
199
+ from loggerbuf.telemetry import TelemetryLog
200
+ from loggerbuf import schema_loader
201
+
202
+ main_data_pb2 = schema_loader.get_main_data_pb2()
203
+ registry_pb2 = schema_loader.get_registry_pb2()
156
204
 
157
- # 1. Initialize Logger
158
205
  telemetry = TelemetryLog("MAIN")
159
206
 
160
- # 2. Create and populate Event
161
- event = Event()
162
- event.event_type = EventType.EVENT_DATA_BASE_PROCESSING
163
- event.general_note = "User successfully registered"
164
- event.status = EventStatus.STATUS_COMPLETED
207
+ event = main_data_pb2.Event()
208
+ event.event_type = registry_pb2.EventType.EVENT_DATA_BASE_PROCESSING
209
+ event.general_note = "User uploaded a new invoice"
210
+ event.status = registry_pb2.EventStatus.STATUS_PENDING
165
211
 
166
- # Send it to the async binary queue
212
+ # Fire the event asynchronously
167
213
  telemetry.send(event)
168
214
  ```
169
215
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "loggerbuf"
7
- version = "0.9.2"
7
+ version = "0.9.3"
8
8
  description = "A powerful structured telemetry and debugging logger with Protobuf schemas"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -1,8 +1,9 @@
1
+ # WARNING: DO NOT EDIT THIS FILE MANUALLY.
1
2
  # -*- coding: utf-8 -*-
2
3
  # Generated by the protocol buffer compiler. DO NOT EDIT!
3
4
  # NO CHECKED-IN PROTOBUF GENCODE
4
5
  # source: main_data.proto
5
- # Protobuf Python Version: 6.33.4
6
+ # Protobuf Python Version: 7.35.0
6
7
  """Generated protocol buffer code."""
7
8
  from google.protobuf import descriptor as _descriptor
8
9
  from google.protobuf import descriptor_pool as _descriptor_pool
@@ -11,9 +12,9 @@ from google.protobuf import symbol_database as _symbol_database
11
12
  from google.protobuf.internal import builder as _builder
12
13
  _runtime_version.ValidateProtobufRuntimeVersion(
13
14
  _runtime_version.Domain.PUBLIC,
14
- 6,
15
- 33,
16
- 4,
15
+ 7,
16
+ 35,
17
+ 0,
17
18
  '',
18
19
  'main_data.proto'
19
20
  )
@@ -22,10 +23,13 @@ _runtime_version.ValidateProtobufRuntimeVersion(
22
23
  _sym_db = _symbol_database.Default()
23
24
 
24
25
 
25
- from . import registry_pb2 as registry__pb2
26
+ try:
27
+ from . import registry_pb2 as registry__pb2
28
+ except ImportError:
29
+ import registry_pb2 as registry__pb2
26
30
 
27
31
 
28
- DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0fmain_data.proto\x12\tmain_data\x1a\x0eregistry.proto\"\xc0\x02\n\x05\x45vent\x12+\n\nevent_type\x18\x01 \x01(\x0e\x32\x17.enum_control.EventType\x12\x11\n\ttimestamp\x18\x02 \x01(\t\x12\x14\n\x0cgeneral_note\x18\x03 \x01(\t\x12)\n\x06status\x18\x04 \x01(\x0e\x32\x19.enum_control.EventStatus\x12\x13\n\x0blogger_name\x18\x06 \x01(\t\x12\x13\n\x0b\x63\x61ller_file\x18\x07 \x01(\t\x12\x14\n\x0c\x63\x61ller_class\x18\x08 \x01(\t\x12\x17\n\x0f\x63\x61ller_function\x18\t \x01(\t\x12\x0e\n\x06lineno\x18\n \x01(\x05\x12\x1b\n\x12previous_file_hash\x18\x8dN \x01(\x0c\x12\x17\n\x0eis_chain_start\x18\x8eN \x01(\x08\x12\x17\n\x0ehmac_signature\x18\x8fN \x01(\x0c\"\x99\x02\n\x0c\x43ounterEvent\x12/\n\x0c\x63ounter_type\x18\x01 \x01(\x0e\x32\x19.enum_control.CounterType\x12\x11\n\ttimestamp\x18\x02 \x01(\t\x12\r\n\x05\x63ount\x18\x03 \x01(\x05\x12\x13\n\x0blogger_name\x18\x04 \x01(\t\x12\x13\n\x0b\x63\x61ller_file\x18\x05 \x01(\t\x12\x14\n\x0c\x63\x61ller_class\x18\x06 \x01(\t\x12\x17\n\x0f\x63\x61ller_function\x18\x07 \x01(\t\x12\x0e\n\x06lineno\x18\x08 \x01(\x05\x12\x1b\n\x12previous_file_hash\x18\x8dN \x01(\x0c\x12\x17\n\x0eis_chain_start\x18\x8eN \x01(\x08\x12\x17\n\x0ehmac_signature\x18\x8fN \x01(\x0c\x62\x06proto3')
32
+ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0fmain_data.proto\x12\tmain_data\x1a\x0eregistry.proto\"\xd5\x02\n\x05\x45vent\x12+\n\nevent_type\x18\x01 \x01(\x0e\x32\x17.enum_control.EventType\x12\x11\n\ttimestamp\x18\x02 \x01(\t\x12\x14\n\x0cgeneral_note\x18\x03 \x01(\t\x12)\n\x06status\x18\x04 \x01(\x0e\x32\x19.enum_control.EventStatus\x12\x13\n\x0blogger_name\x18\x06 \x01(\t\x12\x13\n\x0b\x63\x61ller_file\x18\x07 \x01(\t\x12\x14\n\x0c\x63\x61ller_class\x18\x08 \x01(\t\x12\x17\n\x0f\x63\x61ller_function\x18\t \x01(\t\x12\x0e\n\x06lineno\x18\n \x01(\x05\x12\x13\n\x0b\x64uration_ms\x18\x0b \x01(\x03\x12\x1b\n\x12previous_file_hash\x18\x8dN \x01(\x0c\x12\x17\n\x0eis_chain_start\x18\x8eN \x01(\x08\x12\x17\n\x0ehmac_signature\x18\x8fN \x01(\x0c\"\x99\x02\n\x0c\x43ounterEvent\x12/\n\x0c\x63ounter_type\x18\x01 \x01(\x0e\x32\x19.enum_control.CounterType\x12\x11\n\ttimestamp\x18\x02 \x01(\t\x12\r\n\x05\x63ount\x18\x03 \x01(\x05\x12\x13\n\x0blogger_name\x18\x04 \x01(\t\x12\x13\n\x0b\x63\x61ller_file\x18\x05 \x01(\t\x12\x14\n\x0c\x63\x61ller_class\x18\x06 \x01(\t\x12\x17\n\x0f\x63\x61ller_function\x18\x07 \x01(\t\x12\x0e\n\x06lineno\x18\x08 \x01(\x05\x12\x1b\n\x12previous_file_hash\x18\x8dN \x01(\x0c\x12\x17\n\x0eis_chain_start\x18\x8eN \x01(\x08\x12\x17\n\x0ehmac_signature\x18\x8fN \x01(\x0c\x62\x06proto3')
29
33
 
30
34
  _globals = globals()
31
35
  _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
@@ -33,7 +37,7 @@ _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'main_data_pb2', _globals)
33
37
  if not _descriptor._USE_C_DESCRIPTORS:
34
38
  DESCRIPTOR._loaded_options = None
35
39
  _globals['_EVENT']._serialized_start=47
36
- _globals['_EVENT']._serialized_end=367
37
- _globals['_COUNTEREVENT']._serialized_start=370
38
- _globals['_COUNTEREVENT']._serialized_end=651
40
+ _globals['_EVENT']._serialized_end=388
41
+ _globals['_COUNTEREVENT']._serialized_start=391
42
+ _globals['_COUNTEREVENT']._serialized_end=672
39
43
  # @@protoc_insertion_point(module_scope)
@@ -1,8 +1,9 @@
1
+ # WARNING: DO NOT EDIT THIS FILE MANUALLY.
1
2
  # -*- coding: utf-8 -*-
2
3
  # Generated by the protocol buffer compiler. DO NOT EDIT!
3
4
  # NO CHECKED-IN PROTOBUF GENCODE
4
5
  # source: registry.proto
5
- # Protobuf Python Version: 6.33.4
6
+ # Protobuf Python Version: 7.35.0
6
7
  """Generated protocol buffer code."""
7
8
  from google.protobuf import descriptor as _descriptor
8
9
  from google.protobuf import descriptor_pool as _descriptor_pool
@@ -11,9 +12,9 @@ from google.protobuf import symbol_database as _symbol_database
11
12
  from google.protobuf.internal import builder as _builder
12
13
  _runtime_version.ValidateProtobufRuntimeVersion(
13
14
  _runtime_version.Domain.PUBLIC,
14
- 6,
15
- 33,
16
- 4,
15
+ 7,
16
+ 35,
17
+ 0,
17
18
  '',
18
19
  'registry.proto'
19
20
  )
@@ -24,7 +25,7 @@ _sym_db = _symbol_database.Default()
24
25
 
25
26
 
26
27
 
27
- DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0eregistry.proto\x12\x0c\x65num_control*\xf0\x01\n\x0b\x45ventStatus\x12\x16\n\x12STATUS_UNSPECIFIED\x10\x00\x12\x12\n\x0eSTATUS_PENDING\x10\x01\x12\x11\n\rSTATUS_ACTIVE\x10\x02\x12\x14\n\x10STATUS_COMPLETED\x10\x03\x12\x11\n\rSTATUS_FAILED\x10\x04\x12\x12\n\x0eSTATUS_ABORTED\x10\x05\x12 \n\x1c\x45XAMPLE_EVENT_STATUS_STARTED\x10\x33\x12\"\n\x1e\x45XAMPLE_EVENT_STATUS_COMPLETED\x10\x34\x12\x1f\n\x1b\x45XAMPLE_EVENT_STATUS_FAILED\x10\x35*\x94\x02\n\tEventType\x12\x15\n\x11\x45VENT_UNSPECIFIED\x10\x00\x12\x11\n\rEVENT_GENERIC\x10\x01\x12\x0e\n\nEVENT_MAIN\x10\x02\x12\x1e\n\x1a\x45VENT_DATA_BASE_PROCESSING\x10\x03\x12\x15\n\x11\x45VENT_API_REQUEST\x10\x04\x12\x16\n\x12\x45VENT_API_RESPONSE\x10\x05\x12\x1d\n\x19\x45XAMPLE_EVENT_API_INVOKED\x10\x65\x12\x1e\n\x1a\x45XAMPLE_EVENT_API_RESPONSE\x10\x66\x12 \n\x1c\x45XAMPLE_EVENT_DATA_PROCESSED\x10h\x12\x1d\n\x19\x45XAMPLE_EVENT_DATA_STORED\x10i*;\n\x0b\x43ounterType\x12\x17\n\x13\x43OUNTER_UNSPECIFIED\x10\x00\x12\x13\n\x0f\x43OUNTER_GENERIC\x10\x01\x62\x06proto3')
28
+ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0eregistry.proto\x12\x0c\x65num_control*\xe7\x01\n\x0b\x45ventStatus\x12\x16\n\x12STATUS_UNSPECIFIED\x10\x00\x12\x12\n\x0eSTATUS_PENDING\x10\x01\x12\x11\n\rSTATUS_ACTIVE\x10\x02\x12\x14\n\x10STATUS_COMPLETED\x10\x03\x12\x11\n\rSTATUS_FAILED\x10\x04\x12\x12\n\x0eSTATUS_ABORTED\x10\x05\x12\x1d\n\x19\x44\x45MO_EVENT_STATUS_STARTED\x10\x33\x12\x1f\n\x1b\x44\x45MO_EVENT_STATUS_COMPLETED\x10\x34\x12\x1c\n\x18\x44\x45MO_EVENT_STATUS_FAILED\x10\x35*\x88\x02\n\tEventType\x12\x15\n\x11\x45VENT_UNSPECIFIED\x10\x00\x12\x11\n\rEVENT_GENERIC\x10\x01\x12\x0e\n\nEVENT_MAIN\x10\x02\x12\x1e\n\x1a\x45VENT_DATA_BASE_PROCESSING\x10\x03\x12\x15\n\x11\x45VENT_API_REQUEST\x10\x04\x12\x16\n\x12\x45VENT_API_RESPONSE\x10\x05\x12\x1a\n\x16\x44\x45MO_EVENT_API_INVOKED\x10\x65\x12\x1b\n\x17\x44\x45MO_EVENT_API_RESPONSE\x10\x66\x12\x1d\n\x19\x44\x45MO_EVENT_DATA_PROCESSED\x10h\x12\x1a\n\x16\x44\x45MO_EVENT_DATA_STORED\x10i*;\n\x0b\x43ounterType\x12\x17\n\x13\x43OUNTER_UNSPECIFIED\x10\x00\x12\x13\n\x0f\x43OUNTER_GENERIC\x10\x01\x62\x06proto3')
28
29
 
29
30
  _globals = globals()
30
31
  _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
@@ -32,9 +33,9 @@ _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'registry_pb2', _globals)
32
33
  if not _descriptor._USE_C_DESCRIPTORS:
33
34
  DESCRIPTOR._loaded_options = None
34
35
  _globals['_EVENTSTATUS']._serialized_start=33
35
- _globals['_EVENTSTATUS']._serialized_end=273
36
- _globals['_EVENTTYPE']._serialized_start=276
37
- _globals['_EVENTTYPE']._serialized_end=552
38
- _globals['_COUNTERTYPE']._serialized_start=554
39
- _globals['_COUNTERTYPE']._serialized_end=613
36
+ _globals['_EVENTSTATUS']._serialized_end=264
37
+ _globals['_EVENTTYPE']._serialized_start=267
38
+ _globals['_EVENTTYPE']._serialized_end=531
39
+ _globals['_COUNTERTYPE']._serialized_start=533
40
+ _globals['_COUNTERTYPE']._serialized_end=592
40
41
  # @@protoc_insertion_point(module_scope)
@@ -8,11 +8,40 @@ from .config import ConfigManager, QueueStrategy, ConfigKey
8
8
 
9
9
  from . import schema_loader
10
10
  main_data_pb2 = schema_loader.get_main_data_pb2()
11
+ registry_pb2 = schema_loader.get_registry_pb2()
11
12
  Event = main_data_pb2.Event
12
13
  CounterEvent = main_data_pb2.CounterEvent
13
14
 
14
15
  from .queue_metrics import QueueMetrics, MetricField
15
16
 
17
+ class EventContext:
18
+ def __init__(self, telemetry_instance, event_type, base_kwargs):
19
+ self.telemetry = telemetry_instance
20
+ self.event_type = event_type
21
+ self.kwargs = base_kwargs
22
+ self.start_time = None
23
+
24
+ def attach(self, **new_kwargs):
25
+ """Attaches additional data to the event context."""
26
+ self.kwargs.update(new_kwargs)
27
+
28
+ def __enter__(self):
29
+ self.start_time = time.time()
30
+ return self
31
+
32
+ def __exit__(self, exc_type, exc_value, traceback):
33
+ duration_ms = int((time.time() - self.start_time) * 1000)
34
+ self.kwargs["duration_ms"] = duration_ms
35
+
36
+ if exc_type is not None:
37
+ status = registry_pb2.EventStatus.STATUS_FAILED
38
+ self.kwargs["general_note"] = f"[{exc_type.__name__}] {str(exc_value)} | {self.kwargs.get('general_note', '')}"
39
+ else:
40
+ status = registry_pb2.EventStatus.STATUS_COMPLETED
41
+
42
+ self.telemetry.log_event(event_type=self.event_type, status=status, stack_depth=3, **self.kwargs)
43
+ return False
44
+
16
45
  class BaseSettings:
17
46
  def __init__(self, prefix: str, name: str = None, logs_base_dir: str = ".", backup_dir: str = None, file_size: int = None, backup_count: int = None):
18
47
  config = ConfigManager()
@@ -310,17 +339,67 @@ class TelemetryLog:
310
339
  self.__event_writer = TelemetryLog.__instances[name][1]
311
340
  self.__metric_writer = TelemetryLog.__instances[name][2]
312
341
 
313
- def create_event(self, event: Event):
342
+ def event_context(self, event_type=None, **kwargs):
343
+ """Returns a context manager for tracking an event's duration and status."""
344
+ if event_type is None:
345
+ event_type = registry_pb2.EventType.EVENT_GENERIC
346
+ return EventContext(self, event_type, kwargs)
347
+
348
+ def log_event(self, event_type=None, status=None, stack_depth=2, **kwargs):
349
+ """Convenience method to log an event directly with kwargs."""
350
+ if event_type is None:
351
+ event_type = registry_pb2.EventType.EVENT_GENERIC
352
+ if status is None:
353
+ status = registry_pb2.EventStatus.STATUS_COMPLETED
354
+
355
+ main_event = Event()
356
+ main_event.event_type = event_type
357
+ main_event.status = status
358
+
359
+ # Protected fields that the system handles
360
+ protected_fields = {"logger_name", "timestamp", "caller_file", "caller_class", "caller_function", "lineno"}
361
+
362
+ for key, value in kwargs.items():
363
+ if key in protected_fields:
364
+ continue
365
+
366
+ if hasattr(main_event, key):
367
+ field = getattr(main_event, key)
368
+ if hasattr(field, 'CopyFrom'):
369
+ try:
370
+ field.CopyFrom(value)
371
+ except Exception as e:
372
+ # Fallback for some types or error
373
+ pass
374
+ else:
375
+ try:
376
+ setattr(main_event, key, value)
377
+ except Exception:
378
+ pass
379
+ else:
380
+ # Issue warning via debugger if enabled, or just pass
381
+ pass
382
+
383
+ self.create_event(main_event, stack_depth=stack_depth)
384
+
385
+ def create_event(self, event: Event, stack_depth: int = 1):
314
386
  import sys
315
387
  try:
316
- frame = sys._getframe(1)
317
- event.caller_file = os.path.basename(frame.f_code.co_filename)
318
- event.caller_function = frame.f_code.co_name
319
- event.lineno = frame.f_lineno
320
- if 'self' in frame.f_locals:
321
- event.caller_class = frame.f_locals['self'].__class__.__name__
388
+ # Walk up the stack to find the actual caller outside telemetry.py
389
+ frame = sys._getframe(stack_depth)
390
+ while frame and os.path.basename(frame.f_code.co_filename) == "telemetry.py":
391
+ frame = frame.f_back
392
+
393
+ if frame:
394
+ event.caller_file = os.path.basename(frame.f_code.co_filename)
395
+ event.caller_function = frame.f_code.co_name
396
+ event.lineno = frame.f_lineno
397
+ if 'self' in frame.f_locals:
398
+ event.caller_class = frame.f_locals['self'].__class__.__name__
399
+ else:
400
+ event.caller_class = "None"
322
401
  else:
323
- event.caller_class = "None"
402
+ raise ValueError("No caller frame found")
324
403
  except Exception:
325
404
  event.caller_file = "Unknown"
326
405
  event.caller_function = "Unknown"
@@ -347,7 +426,7 @@ class TelemetryLog:
347
426
  _check_deprecated_fields(event)
348
427
  self.__event_writer.write_event(event)
349
428
 
350
- def increment(self, counter_type, value: int = 1):
429
+ def increment(self, counter_type, value: int = 1, stack_depth: int = 1):
351
430
  if not self.__metric_writer:
352
431
  return
353
432
 
@@ -357,14 +436,20 @@ class TelemetryLog:
357
436
 
358
437
  import sys
359
438
  try:
360
- frame = sys._getframe(1)
361
- event.caller_file = os.path.basename(frame.f_code.co_filename)
362
- event.caller_function = frame.f_code.co_name
363
- event.lineno = frame.f_lineno
364
- if 'self' in frame.f_locals:
365
- event.caller_class = frame.f_locals['self'].__class__.__name__
439
+ frame = sys._getframe(stack_depth)
440
+ while frame and os.path.basename(frame.f_code.co_filename) == "telemetry.py":
441
+ frame = frame.f_back
442
+
443
+ if frame:
444
+ event.caller_file = os.path.basename(frame.f_code.co_filename)
445
+ event.caller_function = frame.f_code.co_name
446
+ event.lineno = frame.f_lineno
447
+ if 'self' in frame.f_locals:
448
+ event.caller_class = frame.f_locals['self'].__class__.__name__
449
+ else:
450
+ event.caller_class = "None"
366
451
  else:
367
- event.caller_class = "None"
452
+ raise ValueError("No caller frame found")
368
453
  except Exception:
369
454
  event.caller_file = "Unknown"
370
455
  event.caller_function = "Unknown"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: loggerbuf
3
- Version: 0.9.2
3
+ Version: 0.9.3
4
4
  Summary: A powerful structured telemetry and debugging logger with Protobuf schemas
5
5
  Author-email: LoggerBuf Team <hello@loggerbuf.dev>
6
6
  Project-URL: Repository, https://github.com/calavraian/LoggerBuf
@@ -28,6 +28,8 @@ Requires-Dist: psutil
28
28
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
29
29
  [![Build Status](https://github.com/calavraian/LoggerBuf/actions/workflows/ci.yml/badge.svg)](https://github.com/calavraian/LoggerBuf/actions)
30
30
 
31
+ **[Visit the official website: loggerbuf.dev](https://loggerbuf.dev)**
32
+
31
33
  </div>
32
34
 
33
35
  🇺🇸 English | 🇪🇸 [Español](README.es.md)
@@ -64,6 +66,15 @@ You are now ready to log! You can also run these sub-commands individually if yo
64
66
 
65
67
  ---
66
68
 
69
+ ## 🤖 AI Agent Integration (Agent Skill)
70
+
71
+ LoggerBuf is incredibly easy for AI coding assistants (like Gemini, Claude, or Cursor) to use!
72
+ We provide a dedicated **Agent Skill** (`SKILL.md`) located in `.agents/skills/loggerbuf/SKILL.md` within this repository.
73
+
74
+ If you are using an agentic AI system that supports custom skills, simply point it to our `SKILL.md`. Your agent will instantly understand the CLI workflow, Schema Management rules, Event formatting guidelines, and DRY Python implementation patterns required to integrate LoggerBuf seamlessly into your project.
75
+
76
+ ---
77
+
67
78
  ## 🤔 Why LoggerBuf? (The Problem & The Solution)
68
79
 
69
80
  **The Problem:**
@@ -169,22 +180,57 @@ The custom formatter instantly reveals the *where* and *what* of any log entry w
169
180
  - `message::> [Payload]`: The actual log message or serialized JSON object.
170
181
 
171
182
  ### 2. Analytical Event Logging (Telemetry)
172
- Telemetry uses Protobuf. Every event you track should be categorized using your custom `EventType` and `EventStatus` enums to ensure consistency across your organization. Just like the debugger, Telemetry automatically injects creation timestamps and routing headers behind the scenes.
183
+ Telemetry uses Protobuf to log structured events. Every event is categorized using `EventType` and `EventStatus`. LoggerBuf automatically injects timestamps, file names, lines, and system identifiers.
184
+
185
+ **Way 1: Standard Method (`log_event`)**
186
+ The simplest way to log an event. You can pass any field defined in your `main_data.proto` as a keyword argument (kwargs).
173
187
 
174
188
  ```python
175
- from telemetry import TelemetryLog
176
- from data_logs import Event, EventType, EventStatus
189
+ from loggerbuf.telemetry import TelemetryLog
190
+
191
+ telemetry = TelemetryLog("MAIN")
192
+
193
+ # Fire an event asynchronously
194
+ telemetry.log_event(
195
+ event_type="EVENT_DATA_BASE_PROCESSING",
196
+ general_note="User uploaded a new invoice",
197
+ status="STATUS_COMPLETED",
198
+ duration_ms=150
199
+ )
200
+ ```
201
+
202
+ **Way 2: Context Manager (`event_context`)**
203
+ Ideal for tracking block execution. It automatically calculates `duration_ms` and will set the status to `STATUS_ERROR` if an unhandled exception occurs inside the block.
204
+
205
+ ```python
206
+ from loggerbuf.telemetry import TelemetryLog
207
+
208
+ telemetry = TelemetryLog("MAIN")
209
+
210
+ with telemetry.event_context(general_note="Processing database migration"):
211
+ # ... your code here ...
212
+ # duration_ms is automatically tracked and the event is fired when exiting the block
213
+ pass
214
+ ```
215
+
216
+ **Way 3: Manual Object Building (`send`)**
217
+ If you need absolute control, you can build the Protobuf object manually:
218
+
219
+ ```python
220
+ from loggerbuf.telemetry import TelemetryLog
221
+ from loggerbuf import schema_loader
222
+
223
+ main_data_pb2 = schema_loader.get_main_data_pb2()
224
+ registry_pb2 = schema_loader.get_registry_pb2()
177
225
 
178
- # 1. Initialize Logger
179
226
  telemetry = TelemetryLog("MAIN")
180
227
 
181
- # 2. Create and populate Event
182
- event = Event()
183
- event.event_type = EventType.EVENT_DATA_BASE_PROCESSING
184
- event.general_note = "User successfully registered"
185
- event.status = EventStatus.STATUS_COMPLETED
228
+ event = main_data_pb2.Event()
229
+ event.event_type = registry_pb2.EventType.EVENT_DATA_BASE_PROCESSING
230
+ event.general_note = "User uploaded a new invoice"
231
+ event.status = registry_pb2.EventStatus.STATUS_PENDING
186
232
 
187
- # Send it to the async binary queue
233
+ # Fire the event asynchronously
188
234
  telemetry.send(event)
189
235
  ```
190
236
 
@@ -96,3 +96,37 @@ def test_telemetry_concurrency_lossless(tmp_path):
96
96
  count += 1
97
97
 
98
98
  assert count == 1000
99
+
100
+ def test_telemetry_log_event(tmp_path):
101
+ settings = EventSettings(name=f"TEST_LOG_EVENT_{tmp_path.name}", logs_base_dir=str(tmp_path))
102
+ telemetry = TelemetryLog(settings)
103
+
104
+ # Test setting a standard field
105
+ telemetry.log_event(general_note="Test log_event")
106
+
107
+ writer = telemetry._TelemetryLog__event_writer
108
+ writer.queue.join()
109
+ time.sleep(0.1)
110
+
111
+ def test_telemetry_event_context_success(tmp_path):
112
+ settings = EventSettings(name=f"TEST_CTX_SUCCESS_{tmp_path.name}", logs_base_dir=str(tmp_path))
113
+ telemetry = TelemetryLog(settings)
114
+
115
+ with telemetry.event_context(general_note="Context Test"):
116
+ time.sleep(0.05)
117
+
118
+ writer = telemetry._TelemetryLog__event_writer
119
+ writer.queue.join()
120
+ time.sleep(0.1)
121
+
122
+ def test_telemetry_event_context_exception(tmp_path):
123
+ settings = EventSettings(name=f"TEST_CTX_FAIL_{tmp_path.name}", logs_base_dir=str(tmp_path))
124
+ telemetry = TelemetryLog(settings)
125
+
126
+ with pytest.raises(ValueError, match="Test Error"):
127
+ with telemetry.event_context(general_note="Exception Test"):
128
+ raise ValueError("Test Error")
129
+
130
+ writer = telemetry._TelemetryLog__event_writer
131
+ writer.queue.join()
132
+ time.sleep(0.1)
File without changes