code: # encoding: utf-8
  #
  #
  # This Source Code Form is subject to the terms of the Mozilla Public
  # License, v. 2.0. If a copy of the MPL was not distributed with this file,
  # You can obtain one at http://mozilla.org/MPL/2.0/.
  #
  # Contact: Kyle Lahnakoski (dummy@email.com)
  #
  from __future__ import absolute_import, division, unicode_literals
  
  import os
  import platform
  import sys
  from datetime import datetime
  
  from mo_dots import Data, FlatList, coalesce, is_data, is_list, listwrap, unwraplist, wrap
  from mo_future import PY3, is_text, text
  from mo_logs import constants, exceptions, strings
  from mo_logs.exceptions import Except, LogItem, suppress_exception
  from mo_logs.strings import CR, indent
  
  _Thread = None
  if PY3:
      STDOUT = sys.stdout.buffer
  else:
      STDOUT = sys.stdout
  
  
  class Log(object):
      """
      FOR STRUCTURED LOGGING AND EXCEPTION CHAINING
      """
      trace = False
      main_log = None
      logging_multi = None
      profiler = None   # simple pypy-friendly profiler
      error_mode = False  # prevent error loops
  
      @classmethod
      def start(cls, settings=None):
          """
          RUN ME FIRST TO SETUP THE THREADED LOGGING
          http://victorlin.me/2012/08/good-logging-practice-in-python/
  
          log       - LIST OF PARAMETERS FOR LOGGER(S)
          trace     - SHOW MORE DETAILS IN EVERY LOG LINE (default False)
          cprofile  - True==ENABLE THE C-PROFILER THAT COMES WITH PYTHON (default False)
                      USE THE LONG FORM TO SET THE FILENAME {"enabled": True, "filename": "cprofile.tab"}
          profile   - True==ENABLE pyLibrary SIMPLE PROFILING (default False) (eg with Profiler("some description"):)
                      USE THE LONG FORM TO SET FILENAME {"enabled": True, "filename": "profile.tab"}
          constants - UPDATE MODULE CONSTANTS AT STARTUP (PRIMARILY INTENDED TO CHANGE DEBUG STATE)
          """
          global _Thread
          if not settings:
              return
          settings = wrap(settings)
  
          Log.stop()
  
          cls.settings = settings
          cls.trace = coalesce(settings.trace, False)
          if cls.trace:
              from mo_threads import Thread as _Thread
              _ = _Thread
  
          # ENABLE CPROFILE
          if settings.cprofile is False:
              settings.cprofile = {"enabled": False}
          elif settings.cprofile is True:
              if isinstance(settings.cprofile, bool):
                  settings.cprofile = {"enabled": True, "filename": "cprofile.tab"}
          if settings.cprofile.enabled:
              from mo_threads import profiles
              profiles.enable_profilers(settings.cprofile.filename)
  
          if settings.profile is True or (is_data(settings.profile) and settings.profile.enabled):
              Log.error("REMOVED 2018-09-02, Activedata revision 3f30ff46f5971776f8ba18")
              # from mo_logs import profiles
              #
              # if isinstance(settings.profile, bool):
              #     profiles.ON = True
              #     settings.profile = {"enabled": True, "filename": "profile.tab"}
              #
              # if settings.profile.enabled:
              #     profiles.ON = True
  
          if settings.constants:
              constants.set(settings.constants)
  
          logs = coalesce(settings.log, settings.logs)
          if logs:
              cls.logging_multi = StructuredLogger_usingMulti()
              for log in listwrap(logs):
                  Log.add_log(Log.new_instance(log))
  
              from mo_logs.log_usingThread import StructuredLogger_usingThread
              cls.main_log = StructuredLogger_usingThread(cls.logging_multi)
  
      @classmethod
      def stop(cls):
          """
          DECONSTRUCTS ANY LOGGING, AND RETURNS TO DIRECT-TO-stdout LOGGING
          EXECUTING MULUTIPLE TIMES IN A ROW IS SAFE, IT HAS NO NET EFFECT, IT STILL LOGS TO stdout
          :return: NOTHING
          """
          main_log, cls.main_log = cls.main_log, StructuredLogger_usingStream(STDOUT)
          main_log.stop()
  
      @classmethod
      def new_instance(cls, settings):
          settings = wrap(settings)
  
          if settings["class"]:
              if settings["class"].startswith("logging.handlers."):
                  from mo_logs.log_usingHandler import StructuredLogger_usingHandler
  
                  return StructuredLogger_usingHandler(settings)
              else:
                  with suppress_exception:
                      from mo_logs.log_usingLogger import make_log_from_settings
  
                      return make_log_from_settings(settings)
                  # OH WELL :(
  
          if settings.log_type == "logger":
              from mo_logs.log_usingLogger import StructuredLogger_usingLogger
              return StructuredLogger_usingLogger(settings)
          if settings.log_type == "file" or settings.file:
              return StructuredLogger_usingFile(settings.file)
          if settings.log_type == "file" or settings.filename:
              return StructuredLogger_usingFile(settings.filename)
          if settings.log_type == "console":
              from mo_logs.log_usingThreadedStream import StructuredLogger_usingThreadedStream
              return StructuredLogger_usingThreadedStream(STDOUT)
          if settings.log_type == "mozlog":
              from mo_logs.log_usingMozLog import StructuredLogger_usingMozLog
              return StructuredLogger_usingMozLog(STDOUT, coalesce(settings.app_name, settings.appname))
          if settings.log_type == "stream" or settings.stream:
              from mo_logs.log_usingThreadedStream import StructuredLogger_usingThreadedStream
              return StructuredLogger_usingThreadedStream(settings.stream)
          if settings.log_type == "elasticsearch" or settings.stream:
              from mo_logs.log_usingElasticSearch import StructuredLogger_usingElasticSearch
              return StructuredLogger_usingElasticSearch(settings)
          if settings.log_type == "email":
              from mo_logs.log_usingEmail import StructuredLogger_usingEmail
              return StructuredLogger_usingEmail(settings)
          if settings.log_type == "ses":
              from mo_logs.log_usingSES import StructuredLogger_usingSES
              return StructuredLogger_usingSES(settings)
          if settings.log_type.lower() in ["nothing", "none", "null"]:
              from mo_logs.log_usingNothing import StructuredLogger
              return StructuredLogger()
  
          Log.error("Log type of {{log_type|quote}} is not recognized", log_type=settings.log_type)
  
      @classmethod
      def add_log(cls, log):
          cls.logging_multi.add_log(log)
  
      @classmethod
      def note(
          cls,
          template,
          default_params={},
          stack_depth=0,
          log_context=None,
          **more_params
      ):
          """
          :param template: *string* human readable string with placeholders for parameters
          :param default_params: *dict* parameters to fill in template
          :param stack_depth:  *int* how many calls you want popped off the stack to report the *true* caller
          :param log_context: *dict* extra key:value pairs for your convenience
          :param more_params: *any more parameters (which will overwrite default_params)
          :return:
          """
          timestamp = datetime.utcnow()
          if not is_text(template):
              Log.error("Log.note was expecting a unicode template")
  
          Log._annotate(
              LogItem(
                  context=exceptions.NOTE,
                  format=template,
                  template=template,
                  params=dict(default_params, **more_params)
              ),
              timestamp,
              stack_depth+1
          )
  
      @classmethod
      def unexpected(
          cls,
          template,
          default_params={},
          cause=None,
          stack_depth=0,
          log_context=None,
          **more_params
      ):
          """
          :param template: *string* human readable string with placeholders for parameters
          :param default_params: *dict* parameters to fill in template
          :param cause: *Exception* for chaining
          :param stack_depth:  *int* how many calls you want popped off the stack to report the *true* caller
          :param log_context: *dict* extra key:value pairs for your convenience
          :param more_params: *any more parameters (which will overwrite default_params)
          :return:
          """
          timestamp = datetime.utcnow()
          if not is_text(template):
              Log.error("Log.warning was expecting a unicode template")
  
          if isinstance(default_params, BaseException):
              cause = default_params
              default_params = {}
  
          if "values" in more_params.keys():
              Log.error("Can not handle a logging parameter by name `values`")
  
          params = Data(dict(default_params, **more_params))
          cause = unwraplist([Except.wrap(c) for c in listwrap(cause)])
          trace = exceptions.get_stacktrace(stack_depth + 1)
  
          e = Except(exceptions.UNEXPECTED, template=template, params=params, cause=cause, trace=trace)
          Log._annotate(
              e,
              timestamp,
              stack_depth+1
          )
  
      @classmethod
      def alarm(
          cls,
          template,
          default_params={},
          stack_depth=0,
          log_context=None,
          **more_params
      ):
          """
          :param template: *string* human readable string with placeholders for parameters
          :param default_params: *dict* parameters to fill in template
          :param stack_depth:  *int* how many calls you want popped off the stack to report the *true* caller
          :param log_context: *dict* extra key:value pairs for your convenience
          :param more_params: more parameters (which will overwrite default_params)
          :return:
          """
          timestamp = datetime.utcnow()
          format = ("*" * 80) + CR + indent(template, prefix="** ").strip() + CR + ("*" * 80)
          Log._annotate(
              LogItem(
                  context=exceptions.ALARM,
                  format=format,
                  template=template,
                  params=dict(default_params, **more_params)
              ),
              timestamp,
              stack_depth + 1
          )
  
      alert = alarm
  
      @classmethod
      def warning(
          cls,
          template,
          default_params={},
          cause=None,
          stack_depth=0,
          log_context=None,
          **more_params
      ):
          """
          :param template: *string* human readable string with placeholders for parameters
          :param default_params: *dict* parameters to fill in template
          :param cause: *Exception* for chaining
          :param stack_depth:  *int* how many calls you want popped off the stack to report the *true* caller
          :param log_context: *dict* extra key:value pairs for your convenience
          :param more_params: *any more parameters (which will overwrite default_params)
          :return:
          """
          timestamp = datetime.utcnow()
          if not is_text(template):
              Log.error("Log.warning was expecting a unicode template")
  
          if isinstance(default_params, BaseException):
              cause = default_params
              default_params = {}
  
          if "values" in more_params.keys():
              Log.error("Can not handle a logging parameter by name `values`")
  
          params = Data(dict(default_params, **more_params))
          cause = unwraplist([Except.wrap(c) for c in listwrap(cause)])
          trace = exceptions.get_stacktrace(stack_depth + 1)
  
          e = Except(exceptions.WARNING, template=template, params=params, cause=cause, trace=trace)
          Log._annotate(
              e,
              timestamp,
              stack_depth+1
          )
  
      @classmethod
      def error(
          cls,
          template,  # human readable template
          default_params={},  # parameters for template
          cause=None,  # pausible cause
          stack_depth=0,
          **more_params
      ):
          """
          raise an exception with a trace for the cause too
  
          :param template: *string* human readable string with placeholders for parameters
          :param default_params: *dict* parameters to fill in template
          :param cause: *Exception* for chaining
          :param stack_depth:  *int* how many calls you want popped off the stack to report the *true* caller
          :param log_context: *dict* extra key:value pairs for your convenience
          :param more_params: *any more parameters (which will overwrite default_params)
          :return:
          """
          if not is_text(template):
              sys.stderr.write(str("Log.error was expecting a unicode template"))
              Log.error("Log.error was expecting a unicode template")
  
          if default_params and isinstance(listwrap(default_params)[0], BaseException):
              cause = default_params
              default_params = {}
  
          params = Data(dict(default_params, **more_params))
  
          add_to_trace = False
          if cause == None:
              causes = None
          elif is_list(cause):
              causes = []
              for c in listwrap(cause):  # CAN NOT USE LIST-COMPREHENSION IN PYTHON3 (EXTRA STACK DEPTH FROM THE IN-LINED GENERATOR)
                  causes.append(Except.wrap(c, stack_depth=1))
              causes = FlatList(causes)
          elif isinstance(cause, BaseException):
              causes = Except.wrap(cause, stack_depth=1)
          else:
              causes = None
              Log.error("can only accept Exception, or list of exceptions")
  
          trace = exceptions.get_stacktrace(stack_depth + 1)
  
          if add_to_trace:
              cause[0].trace.extend(trace[1:])
  
          e = Except(context=exceptions.ERROR, template=template, params=params, cause=causes, trace=trace)
          raise_from_none(e)
  
      @classmethod
      def _annotate(
          cls,
          item,
          timestamp,
          stack_depth
      ):
          """
          :param itemt:  A LogItemTHE TYPE OF MESSAGE
          :param stack_depth: FOR TRACKING WHAT LINE THIS CAME FROM
          :return:
          """
          item.timestamp = timestamp
          item.machine = machine_metadata
          item.template = strings.limit(item.template, 10000)
  
          item.format = strings.limit(item.format, 10000)
          if item.format == None:
              format = text(item)
          else:
              format = item.format.replace("{{", "{{params.")
          if not format.startswith(CR) and format.find(CR) > -1:
              format = CR + format
  
          if cls.trace:
              log_format = item.format = "{{machine.name}} (pid {{machine.pid}}) - {{timestamp|datetime}} - {{thread.name}} - \"{{location.file}}:{{location.line}}\" - ({{location.method}}) - " + format
              f = sys._getframe(stack_depth + 1)
              item.location = {
                  "line": f.f_lineno,
                  "file": text(f.f_code.co_filename),
                  "method": text(f.f_code.co_name)
              }
              thread = _Thread.current()
              item.thread = {"name": thread.name, "id": thread.id}
          else:
              log_format = item.format = "{{timestamp|datetime}} - " + format
  
          cls.main_log.write(log_format, item.__data__())
  
      def write(self):
          raise NotImplementedError
  
  
  def _same_frame(frameA, frameB):
      return (frameA.line, frameA.file) == (frameB.line, frameB.file)
  
  
  # GET THE MACHINE METADATA
  machine_metadata = wrap({
      "pid":  os.getpid(),
      "python": text(platform.python_implementation()),
      "os": text(platform.system() + platform.release()).strip(),
      "name": text(platform.node())
  })
  
  
  def raise_from_none(e):
      raise e
  
  if PY3:
      exec("def raise_from_none(e):\n    raise e from None\n", globals(), locals())
  
  
  from mo_logs.log_usingFile import StructuredLogger_usingFile
  from mo_logs.log_usingMulti import StructuredLogger_usingMulti
  from mo_logs.log_usingStream import StructuredLogger_usingStream
  
  
  if not Log.main_log:
      Log.main_log = StructuredLogger_usingStream(STDOUT)
  
  
entities: [['EMAIL_ADDRESS', 'dummy@email.com'], ['PERSON', 'Kyle Lahnakoski'], ['NRP', 'FlatList'], ['PERSON', 'LogItem'], ['PERSON', 'cprofile  - True==ENABLE'], ['DATE_TIME', '2018-09-02'], ['PERSON', 'mo_logs.log_usingThread'], ['LOCATION', 'main_log'], ['NRP', 'mo_logs.log_usingLogger'], ['LOCATION', 'OH'], ['PERSON', 'LogItem'], ['PERSON', 'LogItem'], ['NRP', 'Log.error("can'], ['PERSON', 'format.find(CR'], ['PERSON', 'same_frame(frameA'], ['URL', 'http://mozilla.org/MPL/2.0/.'], ['URL', 'http://victorlin.me/2012/08/good-logging-practice-in-python/'], ['URL', 'email.com'], ['URL', 'logs.st'], ['URL', 'sys.st'], ['URL', 'sys.st'], ['URL', 'Log.st'], ['URL', 'cls.se'], ['URL', 'cls.tr'], ['URL', 'settings.tr'], ['URL', 'cls.tr'], ['URL', 'settings.cprofile.fi'], ['URL', 'settings.pro'], ['URL', 'settings.pro'], ['URL', 'settings.pro'], ['URL', 'Log.er'], ['URL', 'settings.pro'], ['URL', 'settings.pro'], ['URL', 'settings.pro'], ['URL', 'settings.co'], ['URL', 'constants.se'], ['URL', 'settings.co'], ['URL', 'Log.ad'], ['URL', 'Log.ne'], ['URL', 'cls.ma'], ['URL', 'cls.ma'], ['URL', 'cls.ma'], ['URL', 'log.st'], ['URL', 'settings.fi'], ['URL', 'settings.fi'], ['URL', 'settings.fi'], ['URL', 'settings.fi'], ['URL', 'settings.st'], ['URL', 'settings.st'], ['URL', 'settings.st'], ['URL', 'Log.er'], ['URL', 'multi.ad'], ['URL', 'Log.er'], ['URL', 'Log.no'], ['URL', 'exceptions.NO'], ['URL', 'Log.er'], ['URL', 'params.ke'], ['URL', 'Log.er'], ['URL', 'exceptions.ge'], ['URL', 'exceptions.AL'], ['URL', 'Log.er'], ['URL', 'params.ke'], ['URL', 'Log.er'], ['URL', 'exceptions.ge'], ['URL', 'sys.st'], ['URL', 'Log.er'], ['URL', 'Log.er'], ['URL', 'Log.er'], ['URL', 'Log.er'], ['URL', 'exceptions.ge'], ['URL', 'exceptions.ER'], ['URL', 'item.ma'], ['URL', 'strings.li'], ['URL', 'item.fo'], ['URL', 'strings.li'], ['URL', 'item.fo'], ['URL', 'item.fo'], ['URL', 'item.format.re'], ['URL', 'format.st'], ['URL', 'format.fi'], ['URL', 'cls.tr'], ['URL', 'item.fo'], ['URL', 'machine.na'], ['URL', 'thread.na'], ['URL', 'location.fi'], ['URL', 'location.li'], ['URL', 'location.me'], ['URL', 'code.co'], ['URL', 'code.co'], ['URL', 'Thread.cu'], ['URL', 'item.th'], ['URL', 'thread.na'], ['URL', 'thread.id'], ['URL', 'item.fo'], ['URL', 'cls.ma'], ['URL', 'frameA.li'], ['URL', 'frameA.fi'], ['URL', 'frameB.li'], ['URL', 'frameB.fi'], ['URL', 'os.ge'], ['URL', 'platform.py'], ['URL', 'platform.sy'], ['URL', 'platform.re'], ['URL', 'platform.no'], ['URL', 'Log.ma'], ['URL', 'Log.ma']]