Skip to content

Commit

Permalink
0.2.0 Add Support of GELF UDP (#5)
Browse files Browse the repository at this point in the history
* Support of GELF UDP
* Update docs about GELF UDP
* Bump version
  • Loading branch information
malinkinsa authored Apr 15, 2022
1 parent 189bf32 commit c203224
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 7 deletions.
19 changes: 18 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@ Async python logging handlers that send messages in the Graylog Extended Log For
- [Usage](#usage)
- [GELF TCP](#gelf-tcp)
- [GELF HTTP](#gelf-http)
- [GELF UDP](#gelf-udp)
- [Available params](#available-params)

## List of ready to run GELF handlers
- TCP (with and without TLS);
- HTTP (with and without TLS);
- UDP

## Get AsyncGELF
```python
Expand Down Expand Up @@ -56,12 +58,27 @@ async def main(message):
asyncio.run(main(message))
```

### GELF UDP
```python
import asyncio
import asyncgelf

async def main(message):
handler = asyncgelf.GelfUdp(
host='127.0.0.1',
)

await handler.udp_handler(message)

asyncio.run(main(message))
```

### Available params
- ```host``` Requaried | Graylog server address;
- ```port``` Optional | Graylog input port (default: 12201);
- ```gelf_version``` Optional | GELF spec version (default: 1.1)
- ```level``` Optional | The level equal to the standard syslog levels (default: 1);
- ```scheme``` Optional | HTTP Scheme <i>for GELF HTTP input only</i> (default: http);
- ```tls``` Optional | Path to custom (self-signed) certificate in pem format (default: None)
- ```compress``` Optional | Compress message before sending it to the server or not <i>for GELF HTTP input only</i> (default: False)
- ```compress``` Optional | Compress message before sending it to the server or not (default: False)
- ```debug``` Optional | Additional information in error log (default: False)
2 changes: 1 addition & 1 deletion asyncgelf/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
from .asyncgelf import GelfTcp, GelfHttp
from .asyncgelf import GelfTcp, GelfHttp, GelfUdp
69 changes: 67 additions & 2 deletions asyncgelf/asyncgelf.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import asyncio
import json
import httpx
import json
import math
import os
import socket
import ssl
import struct
import zlib

from typing import Optional

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

return getattr(e, 'message', repr(e))


class GelfUdp(GelfBase):
async def udp_handler(self, message):
"""
UDP handler for send logs to Graylog Input with type: gelf udp
:param message: input message
:return: Message send error in next case: message size more than 1048576 bytes
"""
"""
Declaring limits for GELF messages
"""
max_chunk_size = 8192
max_chunk_count = 128

client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

gelf_message = GelfBase.make(self, message)
bytes_msg = json.dumps(gelf_message).encode('utf-8')

if self.compress:
bytes_msg = zlib.compress(bytes_msg, level=1)
"""
Checking the message size.
"""
if len(bytes_msg) > max_chunk_size:
total_chunks = int(math.ceil(len(bytes_msg) / max_chunk_size))

if total_chunks > max_chunk_count:
return "Error. Your message couldn't be sent because it's too large."

chunks = [bytes(bytes_msg)[i: i + max_chunk_size] for i in range(0, len(bytes(bytes_msg)), max_chunk_size)]

async for i in self.make_gelf_chunks(chunks, total_chunks):
client_socket.sendto(i, (
self.host,
self.port
))

client_socket.sendto(bytes_msg, (
self.host,
self.port
))

async def make_gelf_chunks(self, chunks, total_chunks):
"""
Each chunk is padded with overhead to match the GELF specification.
:param chunks: Chunked gelf_message
:param total_chunks: The total number of chunks a GELF message requires to send
:return:
"""
message_id = os.urandom(8)

for chunk_index, chunk in enumerate(chunks):
yield b''.join((
b'\0x1e\0x0f',
message_id,
struct.pack('b', chunk_index),
struct.pack('b', total_chunks),
chunk
))
6 changes: 3 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@

setup(
name='asyncgelf',
version='0.1.3',
version='0.2.0',
author='Sergey Malinkin',
author_email='malinkinsa@yandex.ru',
url='https://github.com/malinkinsa/asyncgelf',
download_url='https://github.com/malinkinsa/asyncgelf/archive/refs/tags/0.1.3.tar.gz',
download_url='https://github.com/malinkinsa/asyncgelf/archive/refs/tags/0.2.0.tar.gz',
description='Async python logging handlers that send messages in the Graylog Extended Log Format (GELF).',
long_description=open('README.md').read(),
long_description_content_type='text/markdown',
license='MIT',
keywords='gelf logging graylog graylog2 tcp http',
keywords='gelf logging graylog graylog2 tcp udp http',
classifiers=[
'Intended Audience :: Developers',
'License :: OSI Approved :: MIT License',
Expand Down

0 comments on commit c203224

Please sign in to comment.