Skip to content

Commit c203224

Browse files
authored
0.2.0 Add Support of GELF UDP (#5)
* Support of GELF UDP * Update docs about GELF UDP * Bump version
1 parent 189bf32 commit c203224

4 files changed

Lines changed: 89 additions & 7 deletions

File tree

README.md

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,13 @@ Async python logging handlers that send messages in the Graylog Extended Log For
1111
- [Usage](#usage)
1212
- [GELF TCP](#gelf-tcp)
1313
- [GELF HTTP](#gelf-http)
14+
- [GELF UDP](#gelf-udp)
1415
- [Available params](#available-params)
1516

1617
## List of ready to run GELF handlers
1718
- TCP (with and without TLS);
1819
- HTTP (with and without TLS);
20+
- UDP
1921

2022
## Get AsyncGELF
2123
```python
@@ -56,12 +58,27 @@ async def main(message):
5658
asyncio.run(main(message))
5759
```
5860

61+
### GELF UDP
62+
```python
63+
import asyncio
64+
import asyncgelf
65+
66+
async def main(message):
67+
handler = asyncgelf.GelfUdp(
68+
host='127.0.0.1',
69+
)
70+
71+
await handler.udp_handler(message)
72+
73+
asyncio.run(main(message))
74+
```
75+
5976
### Available params
6077
- ```host``` Requaried | Graylog server address;
6178
- ```port``` Optional | Graylog input port (default: 12201);
6279
- ```gelf_version``` Optional | GELF spec version (default: 1.1)
6380
- ```level``` Optional | The level equal to the standard syslog levels (default: 1);
6481
- ```scheme``` Optional | HTTP Scheme <i>for GELF HTTP input only</i> (default: http);
6582
- ```tls``` Optional | Path to custom (self-signed) certificate in pem format (default: None)
66-
- ```compress``` Optional | Compress message before sending it to the server or not <i>for GELF HTTP input only</i> (default: False)
83+
- ```compress``` Optional | Compress message before sending it to the server or not (default: False)
6784
- ```debug``` Optional | Additional information in error log (default: False)

asyncgelf/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
from .asyncgelf import GelfTcp, GelfHttp
1+
from .asyncgelf import GelfTcp, GelfHttp, GelfUdp

asyncgelf/asyncgelf.py

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
import asyncio
2-
import json
32
import httpx
3+
import json
4+
import math
5+
import os
46
import socket
57
import ssl
8+
import struct
9+
import zlib
610

711
from typing import Optional
812

@@ -60,7 +64,7 @@ async def tcp_handler(self, massage):
6064
"""
6165
tcp handler for send logs to Graylog Input with type: gelf tcp
6266
:param massage: input message
63-
:return:
67+
:return: Exception
6468
"""
6569
gelf_message = GelfBase.make(self, massage)
6670
""" Transforming GELF dictionary into bytes """
@@ -163,3 +167,64 @@ async def http_handler(self, message):
163167
return f"{type(e).__name__} at line {e.__traceback__.tb_lineno} of {__file__}: {e}"
164168

165169
return getattr(e, 'message', repr(e))
170+
171+
172+
class GelfUdp(GelfBase):
173+
async def udp_handler(self, message):
174+
"""
175+
UDP handler for send logs to Graylog Input with type: gelf udp
176+
:param message: input message
177+
:return: Message send error in next case: message size more than 1048576 bytes
178+
"""
179+
"""
180+
Declaring limits for GELF messages
181+
"""
182+
max_chunk_size = 8192
183+
max_chunk_count = 128
184+
185+
client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
186+
187+
gelf_message = GelfBase.make(self, message)
188+
bytes_msg = json.dumps(gelf_message).encode('utf-8')
189+
190+
if self.compress:
191+
bytes_msg = zlib.compress(bytes_msg, level=1)
192+
"""
193+
Checking the message size.
194+
"""
195+
if len(bytes_msg) > max_chunk_size:
196+
total_chunks = int(math.ceil(len(bytes_msg) / max_chunk_size))
197+
198+
if total_chunks > max_chunk_count:
199+
return "Error. Your message couldn't be sent because it's too large."
200+
201+
chunks = [bytes(bytes_msg)[i: i + max_chunk_size] for i in range(0, len(bytes(bytes_msg)), max_chunk_size)]
202+
203+
async for i in self.make_gelf_chunks(chunks, total_chunks):
204+
client_socket.sendto(i, (
205+
self.host,
206+
self.port
207+
))
208+
209+
client_socket.sendto(bytes_msg, (
210+
self.host,
211+
self.port
212+
))
213+
214+
async def make_gelf_chunks(self, chunks, total_chunks):
215+
"""
216+
Each chunk is padded with overhead to match the GELF specification.
217+
:param chunks: Chunked gelf_message
218+
:param total_chunks: The total number of chunks a GELF message requires to send
219+
:return:
220+
"""
221+
message_id = os.urandom(8)
222+
223+
for chunk_index, chunk in enumerate(chunks):
224+
yield b''.join((
225+
b'\0x1e\0x0f',
226+
message_id,
227+
struct.pack('b', chunk_index),
228+
struct.pack('b', total_chunks),
229+
chunk
230+
))

setup.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,16 @@
22

33
setup(
44
name='asyncgelf',
5-
version='0.1.3',
5+
version='0.2.0',
66
author='Sergey Malinkin',
77
author_email='malinkinsa@yandex.ru',
88
url='https://github.com/malinkinsa/asyncgelf',
9-
download_url='https://github.com/malinkinsa/asyncgelf/archive/refs/tags/0.1.3.tar.gz',
9+
download_url='https://github.com/malinkinsa/asyncgelf/archive/refs/tags/0.2.0.tar.gz',
1010
description='Async python logging handlers that send messages in the Graylog Extended Log Format (GELF).',
1111
long_description=open('README.md').read(),
1212
long_description_content_type='text/markdown',
1313
license='MIT',
14-
keywords='gelf logging graylog graylog2 tcp http',
14+
keywords='gelf logging graylog graylog2 tcp udp http',
1515
classifiers=[
1616
'Intended Audience :: Developers',
1717
'License :: OSI Approved :: MIT License',

0 commit comments

Comments
 (0)