#!/usr/bin/env python
# -*- coding: utf-8 -*-
# $Id: __init__.py 9439 2015-09-25 08:01:30Z Eric $
#
# Copyright (c) 2015 Nuwa Information Co., Ltd, All Rights Reserved.
#
# Licensed under the Proprietary License,
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at our web site.
#
# See the License for the specific language governing permissions and
# limitations under the License.
#
# $Author: Eric $ (last)
# $Date: 2015-09-25 16:01:30 +0800 (Fri, 25 Sep 2015) $
# $Revision: 9439 $

import logging

from Iuno.cloud import tryGet

logger = logging.getLogger(__name__)

def attachSettings(settingsLocals, backends, preferred, config):
    """
    Attach 'logging' related settings to settings.py.

    @param settingsLocals locals() in settings.py.
    @param backends Backend instances.
    @param preferred Preferred backends.
    @param config Configuration settings.
    """
    # In DEBUG mode (development), we skip cloud logging.
    if settingsLocals.get('DEBUG', False):
        # But if you insist to...
        keepGoing = False if not config else config.get('force', False)
        if not keepGoing:
            logger.warn("Under DEBUG mode we disable cloud logging, use "
                        "'force': True in cloud logging settings "
                        "if you insist.")        
            return

    hostInfo = tryGet('getSentryInfo', backends, preferred, config)
    if not hostInfo:
        logger.error('There are no available sentry servers.')
        return
        
    # Overwrite DSN if user configured it.
    if 'dsn' in config:
        hostInfo.info['SENTRY_DSN'] = config['dsn']
        
    if (not hostInfo.info['SENTRY_DSN'] or 
        hostInfo.info['SENTRY_DSN'].endswith('://')):
        logger.error('There are no available sentry DSN.')
        return        

    from Iuppiter.DjangoUtil import extendInstalledApps

    extendInstalledApps(settingsLocals, (
        'raven.contrib.django.raven_compat',
    ))

    settingsLocals.update({
        'RAVEN_CONFIG': {
            'dsn': hostInfo.info['SENTRY_DSN'],
        },
        'MIDDLEWARE_CLASSES': settingsLocals.get('MIDDLEWARE_CLASSES', ()) + (
            ('raven.contrib.django.raven_compat.middleware.'
             'SentryResponseErrorIdMiddleware'),
        ),
    })

    from raven.contrib.django.handlers import SentryHandler as CloudHandler
    import logging

    handler = CloudHandler()

    if 'level' in config:
        handler.setLevel(logging.getLevelName(config['level']))

    if 'format' in config:
        formatter = logging.Formatter(config['format'])
        handler.setFormatter(formatter)

    from raven.conf import setup_logging
    if 'exclude' in config:
        setup_logging(handler, exclude=config['exclude'])
    else:
        setup_logging(handler)

    hostInfo.backend.attachSettings(settingsLocals)

def attach(project='test', level=None, format=None,
           backendClasses=None, preferred=None, exclude=None,
           dsn=None, attachLoggerName=None, autoLogStacks=False):
    """
    Attach 'logging' related settings to Python runtime environment.
    If your application is not Django related, this function works for you.

    @param project Project key to logging, default 'test' is only for testing.
    @param level Logging level string.
    @param format Logging format.
    @param backendClasses Backend classes, None to use default (predefined)
                          backends.
    @param preferred Preferred backend implementations or machine, ex: {
                         'Amazon': ('i-1ea2b35a',),
                         'Nuwa': ('cloud.tracedig.com', 'naga.servehttp.com'),
                     }
    @param exclude A list of loggers that shouldn't go to logging.
    @param dsn Specified DSN.
    @param attachLoggerName The logger name you want to attach, default is root
                            logger.    
    @param autoLogStacks Automatically log stacks.
    @return Logging handler instance.
    """
    from Iuno.cloud import getBackends

    config = {
        'project': project,
        'dsn': dsn,
    }
    
    backends = getBackends(globals(), backendClasses,
                           preferred=preferred.keys() if preferred else None,
                           config=config)

    hostInfo = tryGet('getSentryInfo', backends, preferred, config)

    if not hostInfo:
        # attachSettings is automatic, so looser than here. attach is called 
        # manually.
        raise RuntimeError('There are no available sentry servers.')

    # Overwrite DSN if user configured it.
    if 'dsn' in config and config['dsn'] is not None:
        hostInfo.info['SENTRY_DSN'] = config['dsn']
        
    if (not hostInfo.info['SENTRY_DSN'] or 
        hostInfo.info['SENTRY_DSN'].endswith('://')):
        raise RuntimeError('There are no available sentry DSN.')
        
    from raven import Client

    client = Client(hostInfo.info['SENTRY_DSN'], auto_log_stacks=autoLogStacks)
    handler = CloudHandler(client)

    if level:
        handler.setLevel(logging.getLevelName(level))

    if format:
        formatter = logging.Formatter(format)
        handler.setFormatter(formatter)

    if attachLoggerName is None or attachLoggerName == 'root':
        from raven.conf import setup_logging
        if exclude:
            setup_logging(handler, exclude=exclude)
        else:
            setup_logging(handler)
            
        logger = logging.getLogger()        
    else:
        logger = logging.getLogger(attachLoggerName)
        if handler.__class__ in map(type, logger.handlers):
            return handler

        logger.addHandler(handler)
    
    if level:
        logger.setLevel(logging.getLevelName(level))

    return handler

def getDefaultPreferred(context=None):
    """
    Get default preferred backend sequence.

    @param context Settings context.
    @return Preferred backend names.
    """
    return (
        'Nuwa',
        'Local'
    )

from raven.handlers.logging import SentryHandler as CloudHandler

def getLogger(name, project=None, level=None, dsn=None, autoLogStacks=False):
    """
    Get cloud logger which can send messages to logging servers.
    
    @param name Logger name.
    @param project Project name to send messages, if None it will try to detect
                   current project, or it will use 'test' project.
    @param level Logging level string.                   
    @param dsn DSN.
    @param autoLogStacks Automatically log stacks.
    """
    loggerName = '[%s]%s' % (__name__, name)
    logger = logging.getLogger(loggerName)
    
    if project is None:
        try:
            from django.conf import settings
            if hasattr(settings, 'IUNO_CLOUD_LOGGING_SETTINGS'):
                _settings = settings.IUNO_CLOUD_LOGGING_SETTINGS
                project = _settings.get('project', None)
                if project is None:
                    dsn = _settings.get('dsn', None)
        except:
            handler = None
            for h in logger.handlers:
                if type(h) == CloudHandler:
                    handler = h
                    break
            if handler: # Attached.
                if level: # Change level to specific level.
                    handler.setLevel(logging.getLevelName(level))
                    logger.setLevel(logging.getLevelName(level))
                return logger
            
            project = 'test'
    
    attach(project=project, level=level, dsn=dsn, 
           attachLoggerName=loggerName, autoLogStacks=autoLogStacks)
    return logger

# Default logger to use 'test' project.
testLogger = getLogger('test', project='test', level='DEBUG', 
                       autoLogStacks=True)
