# -*- coding: utf-8 -*-
from setuptools import setup

package_dir = \
{'': 'src'}

packages = \
['callable_journal']

package_data = \
{'': ['*']}

install_requires = \
['ndl-tools>=0.0.18,<0.0.19',
 'pydantic>=1.6.1,<2.0.0',
 'pyyaml>=5.3.1,<6.0.0',
 'toolz>=0.10.0,<0.11.0']

setup_kwargs = {
    'name': 'callable-journal',
    'version': '0.0.9',
    'description': "Log message generator for a callable's argument and return values.",
    'long_description': '# callable-journal\n[![CI](https://github.com/nathan5280/callable-journal/workflows/Test/badge.svg)](https://github.com/nathan5280/callable-journal/actions)\n[![coverage](https://codecov.io/gh/nathan5280/callable-journal/master/graph/badge.svg)](https://codecov.io/gh/nathan5280/callable-journal)\n[![pypi](https://img.shields.io/pypi/v/callable-journal.svg)](https://pypi.python.org/pypi/callable-journal)\n[![versions](https://img.shields.io/pypi/pyversions/callable-journal.svg)](https://github.com/nathan5280/callable-journal)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/nathan5280/callable-journal/blob/master/LICENSE)\n\nLog message generator for a callable\'s argument and return values.\n\n## Package Motivation\nDo you ever start with something simple like this.\n```python\nfrom typing import List, Tuple\n\ndef motivation(a: int, b: List[int]) -> Tuple[int, List[int]]:\n    multiplied = [a * b_item for b_item in b]\n    return sum(multiplied), multiplied\n```\nRealize you want logging like this.\n```json\n{\n    "tag": "JOURNAL_MSG_JSON",\n    "format": "0.2.0",\n    "objective": "basic",\n    "arguments": {"a": 2, "b": [1, 2]},\n    "results": [6, [2, 4]]\n}\n```\nMess up your function like this.\n```python\nimport copy\nimport logging\nfrom typing import List, Tuple\n\nlogger = logging.getLogger()\n\ndef motivation_with_logging(a: int, b: List[int]) -> Tuple[int, List[int]]:\n    b_copy = copy.deepcopy(b)\n    multiplied = [a * b_item for b_item in b]\n    sum_result = sum(multiplied)\n    msg = {\n        "tag": "JOURNAL_MSG_JSON",\n        "format": "0.2.0",\n        "objective": "basic",\n        "arguments": {"a": a, "b": b_copy},\n        "results": [sum_result, multiplied]\n    }\n    logger.info(msg)\n    return sum(multiplied), multiplied\n```\n\nWouldn\'t it be nice if you could just do this!\n```python\nfrom typing import List, Tuple\nfrom callable_journal import journal\n\n@journal\ndef basic(a: int, b: List[int]) -> Tuple[int, List[int]]:\n    multiplied = [a * b_item for b_item in b]\n    return sum(multiplied), multiplied\n```\n\nThe callable journal decorator does this and a bit more.  It works great on API endpoints \nwhere you want a nice record of what the endpoint was called with and what it returned.  By \nconnecting into the standard library logging package it is easy to format and direct \njournal message to your favorite sink.  \n\n## Naming Results\nIf you have a callable that returns multiple values, you can assign names to them using\nthe `result_names` parameter.\n\n```python\nfrom typing import List, Tuple\nfrom callable_journal import journal\n\n@journal(result_names=["total_sum", "multiplied_values"])\ndef named_results(a: int, b: List[int]) -> Tuple[int, List[int]]:\n    multiplied = [a * b_item for b_item in b]\n    return sum(multiplied), multiplied\n```\n\nLog Message:\n```json\n{\n    "tag": "JOURNAL_MSG_JSON",\n    "format": "0.2.0",\n    "objective": "named_results",\n    "arguments": {"a": 2, "b": [1, 2]},\n    "results": {"total_sum": 6, "multiplied_values": [2, 4]}\n}\n```\n\n### Dropping Results\nIf the result contains sensitive data or not of interest it can be dropped using `DROP_RESULT`.\n\n```python\nfrom typing import List, Tuple\nfrom callable_journal import journal, DROP_RESULT\n\n@journal(result_names=["total_sum", DROP_RESULT])\ndef named_ignore_results(a: int, b: List[int]) -> Tuple[int, List[int]]:\n    multiplied = [a * b_item for b_item in b]\n    return sum(multiplied), multiplied\n```\n\nLog Message:\n```json\n{\n    "tag": "JOURNAL_MSG_JSON",\n    "format": "0.2.0",\n    "objective": "named_results",\n    "arguments": {"a": 2, "b": [1, 2]},\n    "results": {"total_sum": 6}\n}\n```\n\n### Copying Arguments\nSometimes a callable mutates its arguments for good or bad reasons.  If the arguments\naren\'t copied the mutated version will show up in the log message.  Because copying some\narguments is costly, you can specify which arguments to copy with the `copy_args` parameter.\n\n```python\nfrom typing import List, Tuple\nfrom callable_journal import journal\n\n@journal(copy_args="b")\ndef copy_args(a: int, b: List[int]) -> Tuple[int, List[int]]:\n    # Modify the mutable argument.\n    b[0], b[1] = -b[0], -b[1]\n    multiplied = [a * b_item for b_item in b]\n    return sum(multiplied), multiplied\n```\n\nLog Message with the correct mutable argument:\n```json\n{\n    "tag": "JOURNAL_MSG_JSON",\n    "format": "0.2.0",\n    "objective": "copy_args",\n    "arguments": {"a": 2, "b": [1, 2]},\n    "results": [-6, [-2, -4]]\n}\n```\n\nYou can use the `COPY_ALL` value for the `copy_args` parameter to copy all the args.\n\n### Dropping Arguments\nIn the same way that sometimes you want to drop results you may want to drop arguments.\nThis is accomplished with the `drop_args` parameter.\n\n```python\nfrom typing import List, Tuple\nfrom callable_journal import journal\n\n@journal(drop_args=["b"])\ndef drop_args(a: int, b: List[int]) -> Tuple[int, List[int]]:\n    multiplied = [a * b_item for b_item in b]\n    return sum(multiplied), multiplied\n```\n\nLog Message:\n```json\n{\n    "tag": "JOURNAL_MSG_JSON",\n    "format": "0.2.0",\n    "objective": "drop_args",\n    "arguments": {"a": 2},\n    "results": [6, [2, 4]]\n}\n```\n\n### Context\nIf you want information about the context of the application the log message was run in you\ncan add context information to the log messages.\n\n```python\nfrom pathlib import Path\nfrom typing import List, Tuple\nfrom callable_journal import journal, journal_init\n\n@journal\ndef add_context(a: int, b: List[int]) -> Tuple[int, List[int]]:\n    multiplied = [a * b_item for b_item in b]\n    return sum(multiplied), multiplied\n\n\ndef run_add_context():\n    context = {"app_version": "0.1.0"}\n    journal_init(Path(__file__).parent / "journal-cfg.yml", context=context)\n    a, b = 2, [1, 2]\n    add_context(a, b)\n```\n\n```json\n{\n    "tag": "JOURNAL_MSG_JSON",\n    "format": "0.2.0",\n    "objective": "add_context",\n    "arguments": {"a": 2, "b": [1, 2]},\n    "results": [6, [2, 4]],\n    "app_version": "0.1.0"\n}\n```\n\n### Exceptions\nUncaught exceptions are going to be raised and reported, but it is nice to get some amount\nof information about the exception in the log message.  An example is shown here.\n\n```json\n{\n    "tag": "JOURNAL_MSG_JSON",\n    "format": "0.2.0",\n    "objective": "exception",\n    "arguments": {"a": 2, "b": [1, 2]},\n    "exception": {\n        "type": "ZeroDivisionError",\n        "msg": "division by zero",\n        "file": "/home/some_user/projects/callable-journal/test/docs_example_test.py",\n        "line": "170"\n    }\n}\n```\n\n### Logging Configuration\nLogging configuration uses the standard library logging configuration.  Here is an example\nthat configures two loggers. One the generates pure JSON log messages and one that generates\nSTRINGY log messages. \n\n```yaml\n---\nversion: 1\ndisable_existing_loggers: false\nformatters:\n  journal-json:\n    (): callable_journal.formatter.JournalFormatter\n    tag: JOURNAL_MSG_JSON\n    format_mode: json\n  journal-stringy:\n    (): callable_journal.formatter.JournalFormatter\n    tag: JOURNAL_MSG_STRINGY\n    format_mode: stringy\nhandlers:\n  journal-json-console:\n    class: logging.StreamHandler\n    level: INFO\n    formatter: journal-json\n    stream: ext://sys.stdout\n  journal-stringy-console:\n    class: logging.StreamHandler\n    level: INFO\n    formatter: journal-stringy\n    stream: ext://sys.stdout\nloggers:\n  journal:\n    level: INFO\n    handlers:\n      - journal-json-console\n      - journal-stringy-console\n    propagate: false\n...\n```\n\nJSON Log Message:\n```json\n{\n    "tag": "JOURNAL_MSG_JSON",\n    "format": "0.2.0",\n    "objective": "log_format",\n    "context": {"app_version": "0.1.0"},\n    "arguments": {"a": 2, "b": [1, 2]},\n    "results": [6, [2, 4]],\n}\n```\n\nSTRINGY Log Message:\n```json\n{\n    "tag": "JOURNAL_MSG_JSON",\n    "format": "0.2.0",\n    "objective": "log_format",\n    "context": "{\\"app_version\\": \\"0.1.0\\"}",\n    "arguments": "{\\"a\\": 2, \\"b\\": [1, 2]}",\n    "results": "[6, [2, 4]]"\n}\n```\n\nWhy the STRINGY format?  If you have many different messages all flowing through to something\nlike BigQuery it is nice to keep BigQuery from exploding the nested JSON in the context, \narguments and results.   It could wind up with a large number of confusingly named columns.\nBy using the STRINGY format all the arguments from different journals go into one column\nand you can use the JSON extract functionality of BigQuery to get what you want out of that\ncolumn\'s JSON.\n\n### Logging Format\nThe loggers are derived from `logging.Formatter` and pass any unused `args` and `kwargs` on to\nthe default Formatter.  By adding a `format` entry to the configuration you can add all\nthe other log message content you are used to seeing by default.\n\nConfiguration:\n```yaml\n---\nversion: 1\ndisable_existing_loggers: false\nformatters:\n  journal-json:\n    (): callable_journal.formatter.JournalFormatter\n    tag: JOURNAL_MSG_JSON\n    format_mode: json\n    format: \'%(asctime)s - %(name)s - %(levelname)s - %(message)s\'\nhandlers:\n  journal-json-console:\n    class: logging.StreamHandler\n    level: INFO\n    formatter: journal-json\n    stream: ext://sys.stdout\nloggers:\n  journal:\n    level: INFO\n    handlers:\n      - journal-json-console\n    propagate: false\n...\n```\n\nLog Message:\n```text\n2020-08-30 15:54:11,813 - journal - INFO - {\n        "tag": "JOURNAL_MSG_JSON",\n        "format": "0.2.0",\n        "objective": "log_format",\n        app_version": "0.1.0",\n        "arguments": {"a": 2, "b": [1, 2]},\n        "results": [6, [2, 4]],\n    }\n```\n\n',
    'author': 'Nate Atkins',
    'author_email': 'atkinsnw@gmail.com',
    'maintainer': None,
    'maintainer_email': None,
    'url': 'https://github.com/nathan5280/ndl-tools',
    'package_dir': package_dir,
    'packages': packages,
    'package_data': package_data,
    'install_requires': install_requires,
    'python_requires': '>=3.7,<4.0',
}


setup(**setup_kwargs)
