Source code for c7n.ufuncs.logsub

# Copyright 2016-2017 Capital One Services, LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Ops feedback via log subscription
"""
from __future__ import absolute_import, division, print_function, unicode_literals

import boto3

import base64
from datetime import datetime
import json
import textwrap
import zlib


config = logs = sns = None


[docs]def init(): global sns, logs, config if sns is None: sns = boto3.client('sns') if logs is None: logs = boto3.client('logs') with open('config.json') as fh: config = json.load(fh)
[docs]def message_event(evt): dt = datetime.fromtimestamp(evt['timestamp'] / 1000.0) return "%s: %s" % ( dt.ctime(), "\n".join(textwrap.wrap(evt['message'], 80)))
[docs]def process_log_event(event, context): """Format log events and relay via sns/email""" init() serialized = event['awslogs'].pop('data') data = json.loads(zlib.decompress( base64.b64decode(serialized), 16 + zlib.MAX_WBITS)) # Fetch additional logs for context (20s window) timestamps = [e['timestamp'] for e in data['logEvents']] start = min(timestamps) - 1000 * 15 end = max(timestamps) + 1000 * 5 events = logs.get_log_events( logGroupName=data['logGroup'], logStreamName=data['logStream'], startTime=start, endTime=end, startFromHead=True)['events'] message = [ "An error was detected", "", "Log Group: %s" % data['logGroup'], "Log Stream: %s" % data['logStream'], "Log Owner: %s" % data['owner'], "", "Log Contents", ""] # We may get things delivered from log sub that are not in log events for evt in data['logEvents']: if evt not in events: events.append(evt) for evt in events: message.append(message_event(evt)) message.append("") params = dict( TopicArn=config['topic'], Subject=config['subject'], Message='\n'.join(message)) sns.publish(**params)
[docs]def get_function(session_factory, name, role, sns_topic, log_groups, subject="Lambda Error", pattern="Traceback"): """Lambda function provisioning. Self contained within the component, to allow for easier reuse. """ # Lazy import to avoid runtime dependency from c7n.mu import ( LambdaFunction, PythonPackageArchive, CloudWatchLogSubscription) config = dict( name=name, handler='logsub.process_log_event', runtime='python2.7', memory_size=512, timeout=15, role=role, description='Custodian Ops Error Notify', events=[ CloudWatchLogSubscription( session_factory, log_groups, pattern)]) archive = PythonPackageArchive() archive.add_py_file(__file__) archive.add_contents( 'config.json', json.dumps({ 'topic': sns_topic, 'subject': subject })) archive.close() return LambdaFunction(config, archive)