Gilles has uploaded a new change for review. https://gerrit.wikimedia.org/r/234157
Change subject: Send image varnish frontend data from logs to statsd ...................................................................... Send image varnish frontend data from logs to statsd This is a modified version of varnishrls for varnish thumbnail access stats Bug: T105681 Change-Id: Iae36f1916f28d568c5a9c76f07c55699a092b63c --- M modules/role/manifests/cache/statsd.pp A modules/varnish/files/varnishmedia A modules/varnish/manifests/logging/media.pp A modules/varnish/templates/initscripts/varnishmedia.systemd.erb 4 files changed, 194 insertions(+), 0 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/operations/puppet refs/changes/57/234157/1 diff --git a/modules/role/manifests/cache/statsd.pp b/modules/role/manifests/cache/statsd.pp index e631820..927fef6 100644 --- a/modules/role/manifests/cache/statsd.pp +++ b/modules/role/manifests/cache/statsd.pp @@ -23,4 +23,9 @@ ::varnish::logging::rls { 'rls': statsd_server => 'statsd.eqiad.wmnet', } + + # Media browser cache hit rate and request volume stats. + ::varnish::logging::media { 'media': + statsd_server => 'statsd.eqiad.wmnet', + } } diff --git a/modules/varnish/files/varnishmedia b/modules/varnish/files/varnishmedia new file mode 100755 index 0000000..6e58ea8 --- /dev/null +++ b/modules/varnish/files/varnishmedia @@ -0,0 +1,140 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" + varnishmedia + ~~~~~~~~~~~~ + + Accumulate browser cache hit ratio and total request volume statistics + for media requests and report to StatsD. + + Usage: varnishmedia [--statsd-server SERVER] [--key-prefix PREFIX] + + --statsd-server SERVER statsd server (default: none; echo to stdout) + --key-prefix PREFIX metric key prefix (default: media.thumbnail.varnish) + + Copyright 2015 Ori Livneh <o...@wikimedia.org> + Copyright 2015 Gilles Dubuc <gil...@wikimedia.org> + + 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. + +""" +from __future__ import division + +import argparse +import io +import re +import socket +import urlparse + +import varnishlog + +class MediaVarnishLogProcessor: + + def __init__(self, key_prefix='media.thumbnail.varnish', statsd_server=None): + self.key_prefix = key_prefix + self.statsd_server = statsd_server + if statsd_server: + self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + self.stats = {} + self.transactions = {} + + def handle_log_record(self, transaction_id, tag, record, remote_party): + """VSL_handler_f callback function.""" + + if tag == 'RxURL': + # RxURL is the first tag we expect. If there are any existing + # records for this transaction ID, we clear them away. + self.transactions[transaction_id] = {tag: record} + elif tag == 'ReqEnd': + # ReqEnd is the last tag we expect. We pop the transaction's + # records from the buffer and process it. + transaction = self.transactions.pop(transaction_id, None) + if transaction is not None: + transaction[tag] = record + self.process_transaction(transaction) + else: + # All other tags are buffered. + transaction = self.transactions.get(transaction_id) + if transaction is not None: + transaction[tag] = record + return 0 + + def process_transaction(self, transaction): + """Process a single completed transaction.""" + status_code = transaction['TxStatus'] + metric_keys = ['reqs.all', 'resps.' + status_code] + + if 'RxHeader' in transaction: + metric_keys.append('reqs.if_none_match') + + if 'TxHeader' in transaction: + cache_control_header = transaction.get('TxHeader') + cache_control = 'no' + if cache_control_header: + match = re.search(r'(?<=max-age=)\d+', cache_control_header) + if match: + cache_control = 'short' if match.group() == '300' else 'long' + metric_keys.append('responses.%s_cache_control.%s' % + (cache_control, status_code)) + + for key in metric_keys: + self.stats[key] = self.stats.get(key, 0) + 1 + + if self.stats['reqs.all'] > 1: + self.flush_stats() + + def flush_stats(self): + """Flush metrics to standard out or statsd server.""" + buf = io.BytesIO() + while self.stats: + key, value = self.stats.popitem() + metric = '%s.%s:%s|c\n' % (self.key_prefix, key, value) + buf.write(metric.encode('utf-8')) + buf.seek(io.SEEK_SET) + if self.statsd_server: + self.sock.sendto(buf.read(), self.statsd_server) + else: + print(buf.read().decode('utf-8').rstrip()) + + def start(self): + varnishlog.varnishlog(( + ('m', 'RxURL:/thumb/'), # Only look at thumb requests + ('n', 'frontend'), # Consider the frontend Varnish instance + ('i', 'TxStatus'), # Get TxStatus for the HTTP status code + ('i', 'RxURL'), # Get RxURL to match /w/load.php + ('i', 'ReqEnd'), # Get ReqEnd to delimit requests + ), self.handle_log_record) + +def parse_statsd_server_string(server_string): + parsed = urlparse.urlparse('//' + server_string) + return parsed.hostname, parsed.port or 8125 + + +def parse_prefix_string(key_prefix): + key_prefix = key_prefix.strip('.') + if not key_prefix: + raise ValueError('Key prefix must not be empty') + return key_prefix + +ap = argparse.ArgumentParser( + description='Media Browser Cache Hit Ratio StatsD Reporter', + epilog='If no statsd server is specified, prints stats to stdout instead.' +) +ap.add_argument('--key-prefix', help='metric key prefix', + type=parse_prefix_string, default='media.thumbnail.varnish') +ap.add_argument('--statsd-server', help='statsd server', + type=parse_statsd_server_string, default=None) +args = ap.parse_args() + +lp = MediaVarnishLogProcessor(args.key_prefix, args.statsd_server) +lp.start() diff --git a/modules/varnish/manifests/logging/media.pp b/modules/varnish/manifests/logging/media.pp new file mode 100644 index 0000000..d6c8f33 --- /dev/null +++ b/modules/varnish/manifests/logging/media.pp @@ -0,0 +1,40 @@ +# == Define: varnish::logging::media +# +# Accumulate browser cache hit ratio and total request volume statistics +# for Media requests and report to StatsD. +# +# === Parameters +# +# [*statsd_server*] +# StatsD server address, in "host:port" format. +# Defaults to localhost:8125. +# +# === Examples +# +# varnish::logging::media { +# statsd_server => 'statsd.eqiad.wmnet:8125 +# } +# +define varnish::logging::media( $statsd_server = 'statsd' ) { + include varnish::common + + file { '/usr/local/bin/varnishmedia': + source => 'puppet:///modules/varnish/varnishmedia', + owner => 'root', + group => 'root', + mode => '0555', + require => File['/usr/local/lib/python2.7/dist-packages/varnishlog.py'], + notify => Service['varnishmedia'], + } + + base::service_unit { 'varnishmedia': + ensure => present, + systemd => true, + strict => false, + template_name => 'varnishmedia', + require => File['/usr/local/bin/varnishmedia'], + service_params => { + enable => true, + }, + } +} diff --git a/modules/varnish/templates/initscripts/varnishmedia.systemd.erb b/modules/varnish/templates/initscripts/varnishmedia.systemd.erb new file mode 100644 index 0000000..982573d --- /dev/null +++ b/modules/varnish/templates/initscripts/varnishmedia.systemd.erb @@ -0,0 +1,9 @@ +[Unit] +Description=Media Browser Cache Hit Ratio StatsD Reporter +After=varnish-frontend.service + +[Service] +ExecStart=/usr/local/bin/varnishmedia --statsd-server=<%= @statsd_server %> + +[Install] +WantedBy=multi-user.target -- To view, visit https://gerrit.wikimedia.org/r/234157 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: Iae36f1916f28d568c5a9c76f07c55699a092b63c Gerrit-PatchSet: 1 Gerrit-Project: operations/puppet Gerrit-Branch: production Gerrit-Owner: Gilles <gdu...@wikimedia.org> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits