Logging handler to send logs to your OpenSearch cluster with bulk SSL. Forked from https://github.com/logzio/logzio-python-handler
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

350 lines
12 KiB

8 years ago
9 years ago
4 years ago
8 years ago
3 years ago
7 years ago
7 years ago
7 years ago
7 years ago
8 years ago
8 years ago
3 years ago
  1. [![PyPI version](https://badge.fury.io/py/logzio-python-handler.svg)](https://badge.fury.io/py/logzio-python-handler) [![Build Status](https://travis-ci.org/logzio/logzio-python-handler.svg?branch=master)](https://travis-ci.org/logzio/logzio-python-handler)
  2. # The Logz.io Python Handler
  3. <table><tr><th>
  4. ### Deprecation announcement
  5. Version 3.0.0 of this project ends support for Python 2.7, 3.3, and 3.4. We recommend migrating your projects to Python 3.5 or newer as soon as possible. We'll be happy to answer any questions you have in [a GitHub issue](https://github.com/logzio/logzio-python-handler/issues).
  6. Thanks! <br>
  7. The Logz.io Integrations team
  8. </th></tr></table>
  9. This is a Python handler that sends logs in bulk over HTTPS to Logz.io.
  10. The handler uses a subclass named LogzioSender (which can be used without this handler as well, to ship raw data).
  11. The LogzioSender class opens a new Thread, that consumes from the logs queue. Each iteration (its frequency of which can be configured by the logs_drain_timeout parameter), will try to consume the queue in its entirety.
  12. Logs will get divided into separate bulks, based on their size.
  13. LogzioSender will check if the main thread is alive. In case the main thread quits, it will try to consume the queue one last time, and then exit. So your program can hang for a few seconds, until the logs are drained.
  14. In case the logs failed to be sent to Logz.io after a couple of tries, they will be written to the local file system. You can later upload them to Logz.io using curl.
  15. ## Installation
  16. ```bash
  17. pip install logzio-python-handler
  18. ```
  19. If you'd like to use [Trace context](https://github.com/logzio/logzio-python-handler#trace-context) then you need to install the OpenTelemetry logging instrumentation dependecy by running the following command:
  20. ```bash
  21. pip install logzio-python-handler[opentelemetry-logging]
  22. ```
  23. ## Tested Python Versions
  24. Travis CI will build this handler and test against:
  25. - "3.5"
  26. - "3.6"
  27. - "3.7"
  28. - "3.8"
  29. - "3.9"
  30. - "3.10"
  31. - "3.11"
  32. We can't ensure compatibility to any other version, as we can't test it automatically.
  33. To run tests:
  34. ```bash
  35. $ pip install tox
  36. $ tox
  37. ...
  38. ```
  39. ## Python configuration
  40. #### Config File
  41. ```python
  42. [handlers]
  43. keys=LogzioHandler
  44. [handler_LogzioHandler]
  45. class=logzio.handler.LogzioHandler
  46. formatter=logzioFormat
  47. args=('token', 'my_type')
  48. [formatters]
  49. keys=logzioFormat
  50. [loggers]
  51. keys=root
  52. [logger_root]
  53. handlers=LogzioHandler
  54. level=INFO
  55. [formatter_logzioFormat]
  56. format={"additional_field": "value"}
  57. ```
  58. *args=() arguments, by order*
  59. - Your logz.io token
  60. - Log type, for searching in logz.io (defaults to "python")
  61. - Time to sleep between draining attempts (defaults to "3")
  62. - Logz.io Listener address (defaults to "https://listener.logz.io:8071")
  63. - Debug flag. Set to True, will print debug messages to stdout. (defaults to "False")
  64. - Backup logs flag. Set to False, will disable the local backup of logs in case of failure. (defaults to "True")
  65. - Network timeout, in seconds, int or float, for sending the logs to logz.io. (defaults to 10)
  66. - Retries number (retry_no, defaults to 4).
  67. - Retry timeout (retry_timeout) in seconds (defaults to 2).
  68. Please note, that you have to configure those parameters by this exact order.
  69. i.e. you cannot set Debug to true, without configuring all of the previous parameters as well.
  70. #### Dict Config
  71. ```python
  72. LOGGING = {
  73. 'version': 1,
  74. 'disable_existing_loggers': False,
  75. 'formatters': {
  76. 'logzioFormat': {
  77. 'format': '{"additional_field": "value"}',
  78. 'validate': False
  79. }
  80. },
  81. 'handlers': {
  82. 'logzio': {
  83. 'class': 'logzio.handler.LogzioHandler',
  84. 'level': 'INFO',
  85. 'formatter': 'logzioFormat',
  86. 'token': '<<LOGZIO-TOKEN>>',
  87. 'logzio_type': 'python-handler',
  88. 'logs_drain_timeout': 5,
  89. 'url': 'https://<<LOGZIO-URL>>:8071',
  90. 'retries_no': 4,
  91. 'retry_timeout': 2,
  92. }
  93. },
  94. 'loggers': {
  95. '': {
  96. 'level': 'DEBUG',
  97. 'handlers': ['logzio'],
  98. 'propagate': True
  99. }
  100. }
  101. }
  102. ```
  103. Replace:
  104. * <<LOGZIO-TOKEN>> - your logz.io account token.
  105. * <<LOGZIO-URL>> - logz.io url, as described [here](https://docs.logz.io/user-guide/accounts/account-region.html#regions-and-urls).
  106. #### Serverless platforms
  107. If you're using a serverless function, you'll need to import and add the LogzioFlusher annotation before your sender function. To do this, in the code sample below, uncomment the `import` statement and the `@LogzioFlusher(logger)` annotation line.
  108. **Note:** For the LogzioFlusher to work properly, you'll need to make sure that the Logz.io. handler is added to the root logger. See the configuration above for an example.
  109. #### Code Example
  110. ```python
  111. import logging
  112. import logging.config
  113. # If you're using a serverless function, uncomment.
  114. # from logzio.flusher import LogzioFlusher
  115. # Say I have saved my dictionary configuration in a variable named 'LOGGING' - see 'Dict Config' sample section
  116. logging.config.dictConfig(LOGGING)
  117. logger = logging.getLogger('superAwesomeLogzioLogger')
  118. # If you're using a serverless function, uncomment.
  119. # @LogzioFlusher(logger)
  120. def my_func():
  121. logger.info('Test log')
  122. logger.warn('Warning')
  123. try:
  124. 1/0
  125. except:
  126. logger.exception("Supporting exceptions too!")
  127. ```
  128. #### Extra Fields
  129. In case you need to dynamic metadata to your logger, other then the constant metadata from the formatter, you can use the "extra" parameter.
  130. All key values in the dictionary passed in "extra" will be presented in Logz.io as new fields in the log you are sending.
  131. Please note, that you cannot override default fields by the python logger (i.e. lineno, thread, etc..)
  132. For example:
  133. ```python
  134. logger.info('Warning', extra={'extra_key':'extra_value'})
  135. ```
  136. #### Dynamic Extra Fields
  137. The following additional code example offers the same functionlites that availavle with the extra parameter to add additional fields to logs. The difference is that it uses logging filters so it will add the fields that are declared key-values from the extra dictionary to every log that's generated after adding the fliter. You can keep updating the logs with additional filters.
  138. ```python
  139. class ExtraFieldsLogFilter(logging.Filter):
  140. def __init__(self, extra: dict, *args, **kwargs):
  141. super().__init__(*args, **kwargs)
  142. self.extra = extra
  143. def filter(self, record):
  144. record.__dict__.update(self.extra)
  145. return True
  146. def main():
  147. logger.info("Test log") # Outputs: {"message":"Test log"}
  148. extra_fields = {"foo":"bar","counter":1}
  149. logger.addFilter(ExtraFieldsLogFilter(extra_fields))
  150. logger.warning("Warning test log") # Outputs: {"message":"Warning test log","foo":"bar","counter":1}
  151. error_fields = {"err_msg":"Failed to run due to exception.","status_code":500}
  152. logger.addFilter(ExtraFieldsLogFilter(error_fields))
  153. logger.error("Error test log") # Outputs: {"message":"Error test log","foo":"bar","counter":1,"err_msg":"Failed to run due to exception.","status_code":500}
  154. ```
  155. ## Django configuration
  156. ```python
  157. LOGGING = {
  158. 'version': 1,
  159. 'disable_existing_loggers': False,
  160. 'formatters': {
  161. 'verbose': {
  162. 'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s'
  163. },
  164. 'logzioFormat': {
  165. 'format': '{"additional_field": "value"}'
  166. }
  167. },
  168. 'handlers': {
  169. 'console': {
  170. 'class': 'logging.StreamHandler',
  171. 'level': 'DEBUG',
  172. 'formatter': 'verbose'
  173. },
  174. 'logzio': {
  175. 'class': 'logzio.handler.LogzioHandler',
  176. 'level': 'INFO',
  177. 'formatter': 'logzioFormat',
  178. 'token': 'token',
  179. 'logzio_type': "django",
  180. 'logs_drain_timeout': 5,
  181. 'url': 'https://listener.logz.io:8071',
  182. 'debug': True,
  183. 'network_timeout': 10,
  184. },
  185. },
  186. 'loggers': {
  187. 'django': {
  188. 'handlers': ['console', ],
  189. 'level': os.getenv('DJANGO_LOG_LEVEL', 'INFO')
  190. },
  191. 'appname': {
  192. 'handlers': ['console', 'logzio'],
  193. 'level': 'INFO'
  194. }
  195. }
  196. }
  197. ```
  198. *Change*
  199. - token - Your logzio token
  200. - url - Logz.io Listener address
  201. - logs_drain_count - Number of logs to keep in buffer before draining
  202. - logs_drain_timeout - Time to wait before draining, regardless of the previouse setting
  203. - logzio_type - Log type, for searching in logz.io (defaults to "python"), it cannot contain a space.
  204. - appname - Your django app
  205. ## Trace context
  206. If you're sending traces with OpenTelemetry instrumentation (auto or manual), you can correlate your logs with the trace context.
  207. That way, your logs will have traces data in it, such as service name, span id and trace id.
  208. Make sure to install the OpenTelemetry logging instrumentation dependecy by running the following command:
  209. ```bash
  210. pip install logzio-python-handler[opentelemetry-logging]
  211. ```
  212. To enable this feature, set the `add_context` param in your handler configuration to `True`, like in this example:
  213. ```python
  214. LOGGING = {
  215. 'version': 1,
  216. 'disable_existing_loggers': False,
  217. 'formatters': {
  218. 'logzioFormat': {
  219. 'format': '{"additional_field": "value"}',
  220. 'validate': False
  221. }
  222. },
  223. 'handlers': {
  224. 'logzio': {
  225. 'class': 'logzio.handler.LogzioHandler',
  226. 'level': 'INFO',
  227. 'formatter': 'logzioFormat',
  228. 'token': '<<LOGZIO-TOKEN>>',
  229. 'logzio_type': 'python-handler',
  230. 'logs_drain_timeout': 5,
  231. 'url': 'https://<<LOGZIO-URL>>:8071',
  232. 'retries_no': 4,
  233. 'retry_timeout': 2,
  234. 'add_context': True
  235. }
  236. },
  237. 'loggers': {
  238. '': {
  239. 'level': 'DEBUG',
  240. 'handlers': ['logzio'],
  241. 'propagate': True
  242. }
  243. }
  244. }
  245. ```
  246. Please note that if you are using `python 3.8`, it is preferred to use the `logging.config.dictConfig` method, as mentioned in [python's documentation](https://docs.python.org/3/library/logging.config.html#configuration-file-format).
  247. ## Release Notes
  248. - 4.0.2
  249. - Fix bug for logging exceptions ([#76](https://github.com/logzio/logzio-python-handler/pull/76))
  250. - 4.0.1
  251. - Updated `protobuf>=3.20.2`.
  252. - Added dependency `setuptools>=65.5.1`
  253. - 4.0.0
  254. - Add ability to automatically attach trace context to the logs.
  255. <details>
  256. <summary markdown="span"> Expand to check old versions </summary>
  257. - 3.1.1
  258. - Bug fixes (issue #68, exception message formatting)
  259. - Added CI: Tests and Auto release
  260. - 3.1.0
  261. - Bug fixes
  262. - Retry number and timeout is now configurable
  263. - 3.0.0
  264. - Deprecated `python2.7` & `python3.4`
  265. - Changed log levels on `_flush_queue()` method (@hilsenrat)
  266. - 2.0.15
  267. - Added flusher decorator for serverless platforms(@mcmasty)
  268. - Add support for `python3.7` and `python3.8`
  269. - 2.0.13
  270. - Add support for `pypy` and `pypy3`(@rudaporto-olx)
  271. - Add timeout for requests.post() (@oseemann)
  272. - 2.0.12 - Support disable logs local backup
  273. - 2.0.11 - Completely isolate exception from the message
  274. - 2.0.10 - Not ignoring formatting on exceptions
  275. - 2.0.9 - Support extra fields on exceptions too (Thanks @asafc64!)
  276. - 2.0.8 - Various PEP8, testings and logging changes (Thanks @nir0s!)
  277. - 2.0.7 - Make sure sending thread is alive after fork (Thanks @jo-tham!)
  278. - 2.0.6 - Add "flush()" method to manually drain the queue (Thanks @orenmazor!)
  279. - 2.0.5 - Support for extra fields
  280. - 2.0.4 - Publish package as source along wheel, and supprt python3 packagin (Thanks @cchristous!)
  281. - 2.0.3 - Fix bug that consumed more logs while draining than Logz.io's bulk limit
  282. - 2.0.2 - Support for formatted messages (Thanks @johnraz!)
  283. - 2.0.1 - Added __all__ to __init__.py, so support * imports
  284. - 2.0.0 - Production, stable release.
  285. - *BREAKING* - Configuration option logs_drain_count was removed, and the order of the parameters has changed for better simplicity. Please review the parameters section above.
  286. - Introducing the LogzioSender class, which is generic and can be used without the handler wrap to ship raw data to Logz.io. Just create a new instance of the class, and use the append() method.
  287. - Simplifications and Robustness
  288. - Full testing framework
  289. - 1.X - Beta versions
  290. </details>