{ "info": { "author": "Matthew Miguel", "author_email": "mmiguel6288code@gmail.com", "bugtrack_url": null, "classifiers": [ "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Natural Language :: English", "Operating System :: OS Independent", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy" ], "description": "# logarhythm\n\n[View API documentation](http://htmlpreview.github.io/?https://github.com/mmiguel6288code/logarhythm/blob/master/docs/logarhythm/logarhythm.html)\n\n## Goals\nThe goals of this module are to:\n\n1. Make it hassle free to set up logging in the recommended way for the most common configurations\n\n2. Provide some ways of using logging to support debugging, data collection (telemetry), and profiling\n\n## Logging Improvements\n\n### The default logger should be __name__ not the root logger\nThe recommended convention for naming and organizing loggers in the logging HOWTO documentation is to use \\_\\_name\\_\\_\n\ni.e.\n```\nlogger = logging.getLogger(__name__)\n```\n\nHowever, the default logger returned when no name is provided is the root logger. This can introduce problems because changing settings for the root logger can affect all loggers at every level which may result in seeing unexpected logging messages from imported libraries if those libraries were not careful to set up their logging correctly.\nHaving messages from libraries can inhibit troubleshooting and development of the package or module of interest to the developer.\n\nLogarhythm resolves this by changing the behavior of the logging module when logarhythm is first loaded. logarhythm replaces the logging module-level functions with versions that use __name__ to determine the default logger instead of returning the root logger. The result of this is that badly behaved third party libraries will use these replaced functions which will return loggers specific to the library and prevent logging messages from being mixed together unless deliberately set to do so.\n\nIt is possible to undo this and restore the original logging module functions by calling logarhythm.set_disarm_logging_module(False).\n\n\n### Hassle-free logging to stdout/stderr/files/streams\n\nTo make a logger send messages to an additional streams or files in the standard logging module, one needs to perform several steps including creating one or more handlers, creating formatters, associating each handler with the correct formatter, and adding the handlers to the logger. While this allows for powerful customization, doing the most common situations of stderr/stdout/file/stream should be simpler (i.e. one line).\n\nWith logarhythm, to turn on logging to stderr or stdout, simply toggle the corresponding property:\n\n```\nlogger = logarhythm.getLogger() # corresponds to \\_\\_name\\_\\_ by default\nlogger.stderr = False # turn off logging to stderr (it is on by default)\nlogger.stdout = True # turn on logging to stdout (it is off by default)\n```\n\nFor logging to a file, the procedure is similar to how to normally open files in python:\n\n```\nlogger = logarhythm.getLogger() # corresponds to \\_\\_name\\_\\_ by default\n\n# can use as a context manager (closes file and stops logging at end of block)\nwith logger.file_open('/path/to/file.log'):\n logger.warning('goes to the file')\n\n# can use with a logarhythm file handle object\nfh = logger.file_open('/path/to/file.log')\nlogger.warning('this also goes to the file')\nfh.close() # closes file and stops logging\n```\n\nStreams are handled similarly to files:\n```\nlogger = logarhythm.getLogger() # corresponds to \\_\\_name\\_\\_ by default\n\n#can use with a context manager (stops logging to stream at end of block)\nwith logger.stream_open() as sh:\n logger.warning('goes to the stream')\nprint(sh.getvalue()) # prints the content of the stream\n\n\nfrom io import StringIO\nsio = StringIO()\nsh = logger.stream_open(sio)\nlogger.warning('goes to the stream')\nsh.close() # stops future logging to stream - does not clear/release the stream itself\nassert(sio.getvalue() == sh.getvalue())\n```\n\n### Simplified formatting\n\nIn the standard logging module, every handler must be individually associated with a formatter object via the setFormatter() method. Additionally, for most people, writing the format string requires reviewing the online documentation to determine what formatting variables are present, since these variable names have inconsistent capitalization and non-straightforward names. Finally one must decide how to order all the desired information the format string, and may end up doing this inconsistently from project to project since there is no standard/default way of constructing these strings.\n\nIn logarhythm, each logger has a format assigned to it, and all handlers by default will use the same format. It is still possible in logarhythm to individually assign formatters to handlers if that flexibility is desired. Additionally, there is a utility function called build_format() to automatically construct format strings based on input parameters that specify what information to include or not. These parameters and the valid settings are available by calling help(logarhythm.build_format).\n\nWith build_format(), one can choose to include time as\n\n- \"full\" = YYYY-MM-DD/hh:mm:ss\n\n- \"hms\" = hh:mm:ss\n\n- \"elapsed_msec\" = number of milliseconds since the logging module or logarhythm was loaded\n\n- None = no time\n\nAdditionally, one can add or remove via True/False flags the following information:\n\n- logger_name (default True)\n\n- process_name (default False - useful for multiprocessing)\n\n- thread_name (default False - useful for multithreading)\n\n- level (default True)\n\nTo apply a format to a logger:\n```\nlogger = logarhythm.getLogger()\nlogger.format = build_format(time='elapsed_msec',process_name=True)\n```\n\n### Logger configuration convenience functions\n#### Mode properties\nThe main rationale for using logging as opposed to print statements is that it gives you the power to turn on or off different levels of diagnostic detail depending on where you are in development. In the quest to make the common situation easier, there are a few special attributes that configure multiple aspects of a given logger into a mode to match a development phase.\n\nThe dev_mode property configures the following logger properties suited for development and/or troubleshooting:\n .stderr = True `log` messages are displayed and go to stderr\n .stdout = False\n .auto_debug = True `auto` debug (post-mortem for unhandled exceptions) is turned on\n .level = DEBUG `debug` level messages will show\n .captures_disabled=False `capture` patterns are enabled\n .profiling_disabled=False `profiling` is enabled\n .monitoring_disabled=False `monitoring` is enabled\n .debugging_disabled=False `debugging` through logarhythm breakpoints/captures/callbacks is enabled\n\nThe prod_mode property reduces/eliminates diagnostic data as possible to make the program suitable for production and external users:\n .stderr = True `log` messages are displayed and go to stderr\n .stdout = False\n .auto_debug = False `automatic` debug is disabled\n .end_interactive_mode = False `ending` in interactive mode is disabled\n .level = NOTSET `all` loggers are at the NOTSET level by default (only WARNINGS and higher level messages are displayed)\n .captures_disabled = True `capture` patterns are disabled\n .profiling_disabled = True `profiling` is disabled\n .monitoring_disabled = True `monitoring` is disabled\n .debugging_disabled = True `debugging` through logarhythm breakpoints/captures/callbacks is disabled\n\nFor writing doctests in which it is desired to include logging messages, the doctest_mode is provided.\n .stderr = False `doctests` do not capture stderr, so this is disabled\n .stdout = True `doctests` capture stdout, so this is enabled\n .format = build_format(time=None) `this` removes timestamps which allows for test repeatability\n .debugging_disabled = True `typically` debug mode is not wanted when doctests are being run\n\n#### set(), set_all(), and use()\n\nThe following properties/attributes can be set en masse with the set() function using them as keyword arguments.\n auto_debug\n captures_disabled\n debugging_disabled\n disarm_logging_module\n end_interactive\n format\n level\n profiling_disabled\n monitoring_disabled\n stderr\n stdout\n\nThe set_all() function is the same as set() except it applies the values to the current logger as well as all descendant loggers in the logger tree.\n\nThe use() function temporarily sets these properties to values with the context of a 'with' block. At the end of the block, all attributes are set back to what they were before the 'with' block.\n\n## Utilities to use logging to support debugging (with pdb)\n\n### Captures\nWith logarhythm, one can associate a capture pattern (regular expression) that can be triggered when the pattern matches a generated log message.\nThe capture pattern can be specified with a callback to perform any kind of function, however the default behavior is to enter debug mode.\n\n```\nlogger = logarthyhm.getLogger()\nlogger.capture('is 41')\nlogger.level = DEBUG\nfor x in range(100):\n logger.debug('x is %d',x) # will enter debug mode when x is 41\n```\n\n### Monitoring\nWith logarhythm, one can mark an object's attribute to be monitored for changes. When the attribute is changed, a log message will be generated and an optional callback, if specified, will be called.\n```\nlogger = logarhythm.getLogger()\nobj.x = 5\nlogger.monitor_attr(obj,'x')\nobj.x = 6 # generates a logging message\n```\n\nAdditionally, one can mark a callable (function, object method, class method, static method, or object with __call__() defined) to be monitored. When the callable is called, a log message will be generated, and an optional callback, if specified will be called.\n\n```\nlogger = logarhythm.getLogger()\nlogger.monitor_call(func)\nfunc() # generates a logging message\n```\n\n### Miscellaneous convenience utilities\nThese items are admittedly not directly related to logging, however they are very useful for development and are included for convenience.\n\n## Auto Debug\nOne useful feature in python for development is to automatically enter debug mode (post-mortem) following an unhandled exception.\nThis allows one to use pdb to examine the variables at the time of failure in the failed context as well as in calling stack frames (with the \"up\" command in pdb).\nTo do this in standard python requires importing several modules, writing a handler function using it to replace the default exception handling hook.\n\nIn logarhythm, one can use this feature by calling:\n```\nimport logarhythm;\nlogarhythm.set_auto_debug(True)\n```\n\n## End in Interactive Mode\nOnce a program has finished running it is common during development to occasionally want to inspect or examine some of the generated variables.\nBy default, a python program will terminate when it is finished executing. This can be frustrating for Windows users because the command line window simply disappears if the script was not run from an existing command line instance. Additionally, it can be useful for users on any platform in general to end in interactive mode to write doctests (see the doctestify package on pypi for more convenience functions for making doctests).\n\nIn logarhythm, one can make a script end in interactive mode by calling:\n```\nimport logarhythm;\nlogarhythm.set_end_interactive(True)\n```\n## breakpoint() function\nWhen debugging, one can programmatically enter debug mode by calling pdb.set_trace(). In Python 3.7 additionally a built-in breakpoint() function was introduced to do the same thing.\nlogarhythm provides a similar function for convenience with two extra caveats:\n\n1) the breakpoint() function can take an optional condition e.g. breakpoint(x==41)\n\n2) when invoked, a logging message will be generated saying that a breakpoint was tripped\n\n\n## Utilities for capturing data from your programs\n\n### Capturing structured data (telemetry)\nLogarhythm provides two special functions to store and retrieve structure data to and from log files.\nThe tlm() function takes multiple keyword arguments and values and encodes it into a message that can be sent via one of the logging commands.\nGiven a collection of logging messages that include some of these tlm messages - the parse_tlm function can locate, extract and decode the tlm data points into dictionaries of key-value pairs.\nBoth functions allow an optional tlm_channel field, which is a string label that can be used to filter on specific messages.\n\n```\nlogger.level = INFO\nwith logger.stream_open() as sh:\n logger.info(tlm('channel A',x=5,y=10))\n logger.info(tlm('channel A',x=9,y=11))\n logger.info(tlm(z='trying to be tricky with )00000000_tlm_end inside the data itself'))\n\nassert(list(parse_tlm(sh.getvalue())) == [{'x':5,'y':10},{'x':9,'y':11},{'z':'trying to be tricky with )00000000_tlm_end inside the data itself'}])\nassert(list(parse_tlm(sh.getvalue(),'channel A')) == [{'x':5,'y':10},{'x':9,'y':11}])\n```\n\n### Profiling\nThe python standard library comes with some profiling modules to help identify performance bottlenecks in code.\nlogarhythm provides a convenience function that can wrap around a code block to assess performance within that code block and generate a logging message containing the results.\n```\nlogger.level = INFO\nwith logger.profile():\n do_some_slow_function()\n```\n\n\n\n", "description_content_type": "text/markdown", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/_/logarhythm", "keywords": "logging,debugging,telemetry,postmortem,autodebug,interactive,monitoring,profiling", "license": "MIT", "maintainer": "Matthew Miguel", "maintainer_email": "mmiguel6288code@gmail.com", "name": "logarhythm", "package_url": "https://pypi.org/project/logarhythm/", "platform": "", "project_url": "https://pypi.org/project/logarhythm/", "project_urls": { "Homepage": "https://github.com/_/logarhythm" }, "release_url": "https://pypi.org/project/logarhythm/1.0.1/", "requires_dist": null, "requires_python": "", "summary": "", "version": "1.0.1" }, "last_serial": 5942082, "releases": { "1.0.0": [ { "comment_text": "", "digests": { "md5": "16961974432e5c7096a02f0af49f6a3b", "sha256": "f05077865bbe4c586906e82fae7e392092a793e5150bbe64ce6e7a63482d81db" }, "downloads": -1, "filename": "logarhythm-1.0.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "16961974432e5c7096a02f0af49f6a3b", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 28515, "upload_time": "2019-10-05T22:47:12", "url": "https://files.pythonhosted.org/packages/c7/c4/c2505e073abeba919afe6e28335adab55dd9e85e661e4fa9ec34e41ea1f2/logarhythm-1.0.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "33316a343ca4c67c9d69fa6ae7e9c42f", "sha256": "2bf0ea1f9c29017b2fb9d84594c50045d26248406e4467fced088d95a25afee4" }, "downloads": -1, "filename": "logarhythm-1.0.0.tar.gz", "has_sig": false, "md5_digest": "33316a343ca4c67c9d69fa6ae7e9c42f", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 23174, "upload_time": "2019-10-05T22:47:15", "url": "https://files.pythonhosted.org/packages/36/d5/9b443fbb5f2e2f1e772e1da30dbc528b3196eec3d2e073db25b33652c042/logarhythm-1.0.0.tar.gz" } ], "1.0.1": [ { "comment_text": "", "digests": { "md5": "51a00d73c3ab38b1fb0ecba28deb89ad", "sha256": "9200faa6e312a3623a9b19a5cda11bb590917643b7e05ad07d8acf490fa1accd" }, "downloads": -1, "filename": "logarhythm-1.0.1-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "51a00d73c3ab38b1fb0ecba28deb89ad", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 28684, "upload_time": "2019-10-08T00:12:32", "url": "https://files.pythonhosted.org/packages/9d/59/b343501112e1d4a0b619cd20c43564493d7cac9686fb325428f005088d27/logarhythm-1.0.1-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "d08a52aa20cf57e0c7d13a7fe620b9cb", "sha256": "0f2b73b06bc691ff138df7bc7dfc83838066fade6c385372a0a91bac5d76bc6d" }, "downloads": -1, "filename": "logarhythm-1.0.1.tar.gz", "has_sig": false, "md5_digest": "d08a52aa20cf57e0c7d13a7fe620b9cb", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 23457, "upload_time": "2019-10-08T00:12:37", "url": "https://files.pythonhosted.org/packages/87/a6/c7481a660de72aa0339d2dfcdee1705b911845b8a6911e338bf8767a1797/logarhythm-1.0.1.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "51a00d73c3ab38b1fb0ecba28deb89ad", "sha256": "9200faa6e312a3623a9b19a5cda11bb590917643b7e05ad07d8acf490fa1accd" }, "downloads": -1, "filename": "logarhythm-1.0.1-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "51a00d73c3ab38b1fb0ecba28deb89ad", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 28684, "upload_time": "2019-10-08T00:12:32", "url": "https://files.pythonhosted.org/packages/9d/59/b343501112e1d4a0b619cd20c43564493d7cac9686fb325428f005088d27/logarhythm-1.0.1-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "d08a52aa20cf57e0c7d13a7fe620b9cb", "sha256": "0f2b73b06bc691ff138df7bc7dfc83838066fade6c385372a0a91bac5d76bc6d" }, "downloads": -1, "filename": "logarhythm-1.0.1.tar.gz", "has_sig": false, "md5_digest": "d08a52aa20cf57e0c7d13a7fe620b9cb", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 23457, "upload_time": "2019-10-08T00:12:37", "url": "https://files.pythonhosted.org/packages/87/a6/c7481a660de72aa0339d2dfcdee1705b911845b8a6911e338bf8767a1797/logarhythm-1.0.1.tar.gz" } ] }