This repository has been archived by the owner. It is now read-only.
Permalink
Browse files

Web frontend logging.

  • Loading branch information...
1 parent 8a0b7b1 commit 22a5328422b8b89a2a1732d9c8c96714f44b019a @chromakode chromakode committed Sep 7, 2013
View
@@ -604,6 +604,8 @@ spotlight_interest_nosub_p = .1
# map of comment tree version to how frequently it should be chosen relative to
# the others
comment_tree_version_weights = 1:1, 2:0
+# enables/disables client side logging POSTs to /web/log/...
+frontend_logging = true
# markdown message blurbs for the front page sidebar gold ad.
# use **strong** markup for a larger font, and " \n" (<br>) to separate lines.
goldvertisement_blurbs = "Make reddit better. Try %(reddit_gold)." "This year, give the gift of %(reddit_gold)s.|(and you should probably also give some other, better gifts)"
View
@@ -382,6 +382,9 @@ def make_map():
mc("/try", controller="forms", action="try_compact")
+ mc("/web/log/:level", controller="weblog", action="message",
+ requirements=dict(level="error"))
+
# This route handles displaying the error page and
# graphics used in the 404/500
# error pages. It should likely stay at the top
@@ -70,6 +70,7 @@ def load_controllers():
from mediaembed import MediaembedController
from mediaembed import AdController
from policies import PoliciesController
+ from web import WebLogController
from wiki import WikiController
from wiki import WikiApiController
View
@@ -0,0 +1,64 @@
+# The contents of this file are subject to the Common Public Attribution
+# License Version 1.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://code.reddit.com/LICENSE. The License is based on the Mozilla Public
+# License Version 1.1, but Sections 14 and 15 have been added to cover use of
+# software over a computer network and provide for limited attribution for the
+# Original Developer. In addition, Exhibit A has been modified to be consistent
+# with Exhibit B.
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
+# the specific language governing rights and limitations under the License.
+#
+# The Original Code is reddit.
+#
+# The Original Developer is the Initial Developer. The Initial Developer of
+# the Original Code is reddit Inc.
+#
+# All portions of the code written by reddit are Copyright (c) 2006-2013 reddit
+# Inc. All Rights Reserved.
+###############################################################################
+
+from pylons import g, c, request
+from pylons.i18n import _
+
+from r2.controllers.reddit_base import RedditController, abort_with_error
+from r2.lib.base import abort
+from r2.lib.validator import (
+ validate,
+ VOneOf,
+ VPrintable,
+ VRatelimit,
+ VValidatedJSON,
+)
+
+
+class WebLogController(RedditController):
+ on_validation_error = staticmethod(abort_with_error)
+
+ @validate(
+ VRatelimit(rate_user=False, rate_ip=True, prefix='rate_weblog_'),
+ level=VOneOf('level', ('error',)),
+ logs=VValidatedJSON('logs',
+ VValidatedJSON.ArrayOf(VValidatedJSON.Object({
+ 'msg': VPrintable('msg', max_length=256),
+ 'url': VPrintable('url', max_length=256),
+ }))
+ ),
+ )
+ def POST_message(self, level, logs):
+ # prevent simple CSRF by requiring a custom header
+ if not request.headers.get('X-Loggit'):
+ abort(403)
+
+ uid = c.user._id if c.user_is_loggedin else '-'
+
+ # only accept a maximum of 3 entries per request
+ for log in logs[:3]:
+ g.log.warning('[web frontend] %s: %s | U: %s FP: %s UA: %s',
+ level, log['msg'], uid, log['url'],
+ request.user_agent)
+
+ VRatelimit.ratelimit(rate_user=False, rate_ip=True,
+ prefix="rate_weblog_", seconds=10)
View
@@ -219,6 +219,7 @@ class Globals(object):
live_config_spec = {
ConfigValue.bool: [
'frontpage_dart',
+ 'frontend_logging',
],
ConfigValue.float: [
'spotlight_interest_sub_p',
View
@@ -430,6 +430,7 @@ def use(self):
"lib/jed.js",
"base.js",
"preload.js",
+ "logging.js",
"uibase.js",
"i18n.js",
"utils.js",
@@ -36,6 +36,8 @@
import random
import urlparse
import calendar
+import math
+import time
from pylons import g, c, request
from pylons.i18n import _, ungettext
@@ -150,6 +152,8 @@ def js_config(extra_config=None):
"https_endpoint": is_subdomain(request.host, g.domain) and g.https_endpoint,
# debugging?
"debug": g.debug,
+ "send_logs": g.live_config["frontend_logging"],
+ "server_time": math.floor(time.time()),
"status_msg": {
"fetching": _("fetching title..."),
"submitting": _("submitting..."),
@@ -5,44 +5,40 @@ r.setup = function(config) {
// Set the legacy config global
reddit = config
- _.each(['debug', 'warn', 'error'], function(name) {
- // suppress debug messages unless config.debug is set
- r[name] = (name != 'debug' || config.debug)
- && window.console && console[name]
- ? _.bind(console[name], console)
- : function() {}
- })
+ r.logging.init()
r.config.currentOrigin = location.protocol+'//'+location.host
r.analytics.breadcrumbs.init()
}
-r.setupBackbone = function() {
- Backbone.emulateJSON = true
- Backbone.ajax = function(request) {
- var url = request.url
-
- if (request.type == 'GET') {
- var preloaded = r.preload.read(url)
- if (preloaded != null) {
- request.success(preloaded)
-
- var deferred = new jQuery.Deferred
- deferred.resolve(preloaded)
- return deferred
- }
- }
+r.ajax = function(request) {
+ var url = request.url
- var isLocal = url && (url[0] == '/' || url.lastIndexOf(r.config.currentOrigin, 0) == 0)
- if (isLocal) {
- if (!request.headers) {
- request.headers = {}
- }
- request.headers['X-Modhash'] = r.config.modhash
+ if (request.type == 'GET') {
+ var preloaded = r.preload.read(url)
+ if (preloaded != null) {
+ request.success(preloaded)
+
+ var deferred = new jQuery.Deferred
+ deferred.resolve(preloaded)
+ return deferred
}
+ }
- return Backbone.$.ajax(request)
+ var isLocal = url && (url[0] == '/' || url.lastIndexOf(r.config.currentOrigin, 0) == 0)
+ if (isLocal) {
+ if (!request.headers) {
+ request.headers = {}
+ }
+ request.headers['X-Modhash'] = r.config.modhash
}
+
+ return $.ajax(request)
+}
+
+r.setupBackbone = function() {
+ Backbone.emulateJSON = true
+ Backbone.ajax = r.ajax
}
$(function() {
@@ -0,0 +1,73 @@
+r.logging = {}
+
+r.logging.pageAgeLimit = 5*60 // seconds
+r.logging.sendThrottle = 8 // seconds
+
+r.logging.init = function() {
+ _.each(['debug', 'log', 'warn', 'error'], function(name) {
+ // suppress debug messages unless config.debug is set
+ r[name] = (name != 'debug' || r.config.debug)
+ && window.console && console[name]
+ ? _.bind(console[name], console)
+ : function() {}
+ })
+ r.sendError = r.logging.sendError
+}
+
+r.logging.serverLogger = {
+ logCount: 0,
+ _queuedLogs: [],
+
+ queueLog: function(logData) {
+ if (this.logCount >= 3) {
+ r.warn('Not sending debug log; already sent', this.logCount)
+ return
+ }
+
+ // don't send messages for pages older than 5 minutes to prevent CDN cached
+ // pages from slamming us if we need to turn off logs
+ var pageAge = (new Date / 1000) - r.config.server_time
+ if (Math.abs(pageAge) > r.logging.pageAgeLimit) {
+ r.warn('Not sending debug log; page too old:', pageAge)
+ return
+ }
+
+ if (!r.config.send_logs) {
+ r.warn('Server-side debug logging disabled')
+ return
+ }
+
+ logData.url = window.location.toString()
+ this._queuedLogs.push(logData)
+ this.logCount++
+
+ // defer so that errors get batched until JS yields
+ _.defer(_.bind(function() {
+ this._sendLogs()
+ }, this))
+ },
+
+ _sendLogs: _.throttle(function() {
+ var queueCount = this._queuedLogs.length
+ r.ajax({
+ type: 'POST',
+ url: '/web/log/error.json',
+ data: {logs: JSON.stringify(this._queuedLogs)},
+ headers: {
+ 'X-Loggit': true
+ },
+ success: function() {
+ r.log('Sent', queueCount, 'debug logs to server')
+ },
+ error: function(xhr, err, status) {
+ r.warn('Error sending debug logs to server:', err, ';', status)
+ }
+ })
+ this._queuedLogs = []
+ }, r.logging.sendThrottle * 1000)
+}
+
+r.logging.sendError = function() {
+ r.error.apply(r, arguments)
+ r.logging.serverLogger.queueLog({msg: _.toArray(arguments).join(' ')})
+}

0 comments on commit 22a5328

Please sign in to comment.