diff --git a/README.md b/README.md
index 2bc7c00..9951685 100644
--- a/README.md
+++ b/README.md
@@ -24,7 +24,7 @@ There are some default arguments that you can override. At the very least, you s
log_url: '/log?type=log', // The url to which standard logs are sent
log_level: 1, // The level at which to log. This allows you to keep the calls to the logging in your code and just change this variable to log varying degrees. 1 = only error, 2 = error & log, 3 = error, log & info
native_error:true, // Whether or not to send native js errors as well (using window.onerror).
- use_console:false, // Whether to show a console.error/info/log as well (when a console is present)
+ hijack_console:true, // Hijacks the default console functionality (ie: all your console.error/info/log are belong to us).
query_var: 'msg', // The variable to send the log message through as.
client_info: { // Configuration for what info about the client's browser is logged.
location:true, // The url to the page on which the error occurred.
@@ -44,7 +44,10 @@ The log will be sent to the backend as a normal `POST` request, which might look
$.post('/log?type=error&msg=YOUR_ERROR_MESSAGE');
-`native_error` is set to `true` by default. This means that all browser errors will also be captured and passed to the backend.
+#### Notes
+
+* `native_error` is set to `true` by default. This means that all browser errors will also be captured and passed to the backend.
+* `hijack_console` is set to `true` by default and will log to the server as well as display your normal console.error/info/log. If you wish to keep the functionality for $.error and console.error (for example) separate, set hijack_console to false.
### Backend
@@ -60,4 +63,8 @@ Also included with the distribution is a PHP frontend for parsing and displaying
## Credit
-jQuery.clientSideLogging was developed by [Remy Bach](https://github.com/remybach) (JavaScript) and [Rob Miller](https://github.com/robmiller) (PHP).p
\ No newline at end of file
+jQuery.clientSideLogging was developed by [Remy Bach](https://github.com/remybach) (JavaScript) and [Rob Miller](https://github.com/robmiller) (PHP).
+
+## License
+
+MIT License - [http://remybach.mit-license.org/](http://remybach.mit-license.org/)
diff --git a/clientSideLogging.jquery.json b/clientSideLogging.jquery.json
new file mode 100644
index 0000000..cd819ad
--- /dev/null
+++ b/clientSideLogging.jquery.json
@@ -0,0 +1,30 @@
+{
+ "name": "clientSideLogging",
+ "version": "1.1.2",
+ "title": "jQuery Client Side Logging",
+ "author": {
+ "name": "Rémy Bach",
+ "url": "http://remy.bach.me.uk"
+ },
+ "licenses": [{
+ "type": "MIT",
+ "url": "http://remybach.mit-license.org/"
+ }],
+ "dependencies": {
+ "jquery": ">=1.5"
+ },
+ "description": "This plugin allows you to store front end log/info/error messages on the server (note: you will need to have something set up on your server to handle the actual logging).",
+ "keywords": [
+ "errors",
+ "logging",
+ "debug",
+ "debugging"
+ ],
+ "homepage": "https://github.com/remybach/jQuery.clientSideLogging",
+ "docs": "https://github.com/remybach/jQuery.clientSideLogging",
+ "bugs": "https://github.com/remybach/jQuery.clientSideLogging/issues",
+ "maintainers": [{
+ "name":"Rob Miller",
+ "url":"https://github.com/robmiller"
+ }]
+}
\ No newline at end of file
diff --git a/jQuery.clientSideLogging.js b/jQuery.clientSideLogging.js
index 3213ca9..7218baa 100644
--- a/jQuery.clientSideLogging.js
+++ b/jQuery.clientSideLogging.js
@@ -1,156 +1,200 @@
-/*
- * Title: jQuery Client Side Logging Plugin
- * Author: Rémy Bach
- * Version: 1.0.0
- * License: http://remybach.mit-license.org
- * Url: http://github.com/remybach/jQuery.clientSideLogging
- * Description:
- * This plugin allows you to store front end log/info/error messages on the server (note: you will need to have something set up on your server to handle the actual logging).
- * The idea was born after reading the following article: http://openmymind.net/2012/4/4/You-Really-Should-Log-Client-Side-Error/
- */
-(function($) {
- var defaults = {
- error_url: '/log?type=error',
- info_url: '/log?type=info',
- log_url: '/log?type=log',
- log_level: 1,
- native_error:true,
- use_console:false,
- query_var: 'msg',
- client_info: {
- location:true,
- screen_size:true,
- user_agent:true,
- window_size:true
- }
- };
-
- /**
- * Initializing with custom options. Not strictly necessary, but recommended.
- * @param options The custom options.
- */
- $.clientSideLogging = function(options) {
- $.extend(defaults, options || {});
- };
-
- /**
- * The function that will send error logs to the server. Also logs to the console using console.error() (if available and requested by the user)
- * @param what What you want to be logged (String, or JSON object)
- */
- $.error = function(what) {
- if (defaults.log_level >= 1) {
- _send(defaults.error_url, what);
- }
-
- if(window.console&&window.console.error&&defaults.use_console)console.error(what);
- };
-
- /**
- * The function that will send info logs to the server. Also logs to the console using console.info() (if available and requested by the user)
- * @param what What you want to be logged (String, or JSON object)
- */
- $.info = function(what) {
- if (defaults.log_level >= 3) {
- _send(defaults.info_url, what);
- }
-
- if(window.console&&window.console.info&&defaults.use_console)console.info(what);
- };
-
- /**
- * The function that will send standard logs to the server. Also logs to the console using console.log() (if available and requested by the user)
- * @param what What you want to be logged (String, or JSON object)
- */
- $.log = function(what) {
- if (defaults.log_level >= 2) {
- _send(defaults.log_url, what);
- }
-
- if(window.console&&window.console.log&&defaults.use_console)console.log(what);
- };
-
- // Log errors whenever there's a generic js error on the page.
- window.onerror = function(message, file, line) {
- if (defaults.native_error) {
- _send(defaults.error_url, {
- message: message,
- file: file,
- line: line
- });
- }
- };
-
- /*===== Private Functions =====*/
- /**
- * Send the log information to the server.
- * @param url The url to submit the information to.
- * @param what The information to be logged.
- */
- _send = function(url, what) {
- // If the url already has a ? in it.
- if (url.match(/\?.+$/)) {
- url += '&';
- } else {
- url += '?';
- }
-
- format = 'text';
- if (typeof what === 'object') {
- format = 'json';
-
- // Let's grab the additional logging info before we send this off.
- $.extend(what, _buildClientInfo());
- what = JSON.stringify(what);
- }
-
- url += 'format=' + format + '&' + defaults.query_var + '=' + what;
- $.post(url);
- };
-
- /**
- * Build up an object containing the requested information about the client (as specified in defaults).
- * @return _info The object containing the requested information.
- */
- _buildClientInfo = function() {
- var _info = {};
-
- if (defaults.client_info.user_agent) {
- _info.user_agent = navigator.userAgent;
- }
- if (defaults.client_info.window_size) {
- _info.window_size = $(window).width()+' x '+$(window).height();
- }
- if (defaults.client_info.screen_size) {
- _info.screen_size = window.screen.availWidth+' x '+window.screen.availHeight;
- }
- if (defaults.client_info.location) {
- _info.location = window.location.href;
- }
-
- return _info;
- };
-
- /**
- * Fallback for older browsers that don't implement JSON.stringify
- * @param obj The JSON object to turn into a string.
- * @return A string representation of the JSON object.
- */
- JSON.stringify = JSON.stringify || function (obj) {
- var t = typeof (obj);
- if (t != "object" || obj === null) {
- // simple data type
- if (t == "string") obj = '"'+obj+'"';
- return String(obj);
- } else {
- // recurse array or object
- var n, v, json = [], arr = (obj && obj.constructor == Array);
- for (n in obj) {
- v = obj[n]; t = typeof(v);
- if (t == "string") v = '"'+v+'"';
- else if (t == "object" && v !== null) v = JSON.stringify(v);
- json.push((arr ? "" : '"' + n + '":') + String(v));
- }
- return (arr ? "[" : "{") + String(json) + (arr ? "]" : "}");
- }
- };
-})(jQuery);
\ No newline at end of file
+/*
+ * Title: jQuery Client Side Logging Plugin
+ * Author: Rémy Bach
+ * Version: 1.1.2
+ * License: http://remybach.mit-license.org
+ * Url: http://github.com/remybach/jQuery.clientSideLogging
+ * Description:
+ * This plugin allows you to store front end log/info/error messages on the server (note: you will need to have something set up on your server to handle the actual logging).
+ * The idea was born after reading the following article: http://openmymind.net/2012/4/4/You-Really-Should-Log-Client-Side-Error/
+ */
+(function($) {
+ /*===== Run polyfill for console first. =====*/
+ // Make sure browsers that don't have console don't completely die.
+ if (!window.console) {
+ console = {};
+ }
+ // Make console.* behave like proper Functions in IE.
+ if (window.console && typeof console.log == "object") {
+ $.each(["log","info","warn","error","assert","dir","clear","profile","profileEnd"], function(i, method) {
+ console[method] = $.proxy(console[method], console);
+ });
+ }
+ /*===== End polyfill for console. =====*/
+
+ var defaults = {
+ error_url: '/log/?type=error', // The url to which errors logs are sent
+ info_url: '/log/?type=info', // The url to which info logs are sent
+ log_url: '/log/?type=log', // The url to which standard logs are sent
+ log_level: 1, // The level at which to log. This allows you to keep the calls to the logging in your code and just change this variable to log varying degrees. 1 = only error, 2 = error & log, 3 = error, log & info
+ native_error:true, // Whether or not to send native js errors as well (using window.onerror).
+ hijack_console:true, // Hijacks the default console functionality (ie: all your console.error/info/log are belong to us).
+ query_var: 'msg', // The variable to send the log message through as.
+ client_info: { // Configuration for what info about the client's browser is logged.
+ location:true, // The url to the page on which the error occurred.
+ screen_size:true, // The size of the user's screen (different to the window size because the window might not be maximized)
+ user_agent:true, // The user agent string.
+ window_size:true // The window size.
+ }
+ },
+ original_error = console.error,
+ original_info = console.info,
+ original_log = console.log;
+
+ /**
+ * Initializing with custom options. Not strictly necessary, but recommended.
+ * @param options The custom options.
+ */
+ $.clientSideLogging = function(options) {
+ defaults = $.extend(defaults, options || {});
+
+ // We need to unset these again if they were set the first time.
+ if (!defaults.hijack_console) {
+ console.error = original_error;
+ console.info = original_info;
+ console.log = original_log;
+ } else {
+ console.error = $.error;
+ console.info = $.info;
+ console.log = $.log;
+ }
+ };
+
+ /**
+ * The function that will send error logs to the server. Also logs to the console using console.error() (if available and requested by the user)
+ * @param what What you want to be logged (String, or JSON object)
+ */
+ $.error = function(what) {
+ if (defaults.log_level >= 1) {
+ _send(defaults.error_url, what);
+ }
+
+ if (defaults.hijack_console && original_error && original_error.apply) {
+ original_error.apply(console, arguments);
+ }
+ };
+ console.error = $.error;
+
+ /**
+ * The function that will send info logs to the server. Also logs to the console using console.info() (if available and requested by the user)
+ * @param what What you want to be logged (String, or JSON object)
+ */
+ $.info = function(what) {
+ if (defaults.log_level >= 3) {
+ _send(defaults.info_url, what);
+ }
+
+ if (defaults.hijack_console && original_info && original_info.apply) {
+ original_info.apply(console, arguments);
+ }
+ };
+ console.info = $.info;
+
+ /**
+ * The function that will send standard logs to the server. Also logs to the console using console.log() (if available and requested by the user)
+ * @param what What you want to be logged (String, or JSON object)
+ */
+ $.log = function(what) {
+ if (defaults.log_level >= 2) {
+ _send(defaults.log_url, what);
+ }
+
+ if (defaults.hijack_console && original_log && original_log.apply) {
+ original_log.apply(console, arguments);
+ }
+ };
+ console.log = $.log;
+
+ // Log errors whenever there's a generic js error on the page.
+ window.onerror = function(message, file, line) {
+ if (defaults.native_error) {
+ _send(defaults.error_url, {
+ message: message,
+ file: file,
+ line: line
+ });
+ }
+ };
+
+ /*===== Private Functions =====*/
+ /**
+ * Send the log information to the server.
+ * @param url The url to submit the information to.
+ * @param what The information to be logged.
+ */
+ _send = function(url, what) {
+ url += url.match(/\?.+$/) ? '&' : '?';
+
+ if (typeof what === 'object') {
+ // Let's grab the additional logging info before we send this off.
+ $.extend(what, _buildClientInfo());
+
+ _data = {};
+ _data[defaults.query_var] = JSON.stringify(what);
+
+ $.ajax({
+ type:'POST',
+ url:url+'format=json',
+ data:_data
+ });
+ } else {
+ if (what.indexOf(defaults.error_url) > 0) {
+ // Prevent trying to send error messages to non existing error_url
+ return;
+ }
+ $.post(url+'format=text&' + defaults.query_var + '=' + what);
+ }
+ };
+
+ /**
+ * Build up an object containing the requested information about the client (as specified in defaults).
+ * @return _info The object containing the requested information.
+ */
+ _buildClientInfo = function() {
+ var _info = {};
+
+ if (defaults.client_info.user_agent) {
+ _info.user_agent = navigator.userAgent;
+ }
+ if (defaults.client_info.window_size) {
+ _info.window_size = $(window).width()+' x '+$(window).height();
+ }
+ if (defaults.client_info.screen_size) {
+ _info.screen_size = window.screen.availWidth+' x '+window.screen.availHeight;
+ }
+ if (defaults.client_info.location) {
+ _info.location = window.location.href;
+ }
+
+ return _info;
+ };
+
+ /*===== Compatibility Functions =====*/
+ /**
+ * Fallback for older browsers that don't implement JSON.stringify
+ * @param obj The JSON object to turn into a string.
+ * @return A string representation of the JSON object.
+ */
+ var JSON;
+ if (!JSON) {
+ JSON = {};
+ }
+ JSON.stringify = JSON.stringify || function (obj) {
+ var t = typeof (obj);
+ if (t != "object" || obj === null) {
+ // simple data type
+ if (t == "string") obj = '"'+obj+'"';
+ return String(obj);
+ } else {
+ // recurse array or object
+ var n, v, json = [], arr = (obj && obj.constructor == Array);
+ for (n in obj) {
+ v = obj[n]; t = typeof(v);
+ if (t == "string") v = '"'+v+'"';
+ else if (t == "object" && v !== null) v = JSON.stringify(v);
+ json.push((arr ? "" : '"' + n + '":') + String(v));
+ }
+ return (arr ? "[" : "{") + String(json) + (arr ? "]" : "}");
+ }
+ };
+})(jQuery);
diff --git a/test/css/bootstrap-responsive.css b/log/css/bootstrap-responsive.css
similarity index 100%
rename from test/css/bootstrap-responsive.css
rename to log/css/bootstrap-responsive.css
diff --git a/test/css/bootstrap-responsive.min.css b/log/css/bootstrap-responsive.min.css
similarity index 100%
rename from test/css/bootstrap-responsive.min.css
rename to log/css/bootstrap-responsive.min.css
diff --git a/test/css/bootstrap.css b/log/css/bootstrap.css
similarity index 100%
rename from test/css/bootstrap.css
rename to log/css/bootstrap.css
diff --git a/test/css/bootstrap.min.css b/log/css/bootstrap.min.css
similarity index 100%
rename from test/css/bootstrap.min.css
rename to log/css/bootstrap.min.css
diff --git a/log/css/style.css b/log/css/style.css
new file mode 100644
index 0000000..6b40ec6
--- /dev/null
+++ b/log/css/style.css
@@ -0,0 +1,52 @@
+body { padding:10px; }
+
+.navbar .brand { padding-top:14px; }
+.nav li.error:hover, .nav li.log:hover, .nav li.info:hover,
+.nav li.error.active, .nav li.log.active, .nav li.info.active {
+ margin-bottom:0;
+}
+.nav li.error:hover,
+.nav li.error.active {
+ border-bottom:5px solid #B94A48;
+}
+.nav li.log:hover,
+.nav li.log.active {
+ border-bottom:5px solid #B9D5E2;
+}
+.nav li.info:hover,
+.nav li.info.active {
+ border-bottom:5px solid #BED5BE;
+}
+
+ul { list-style:none; margin-left:5px; }
+li { margin:5px 0; }
+.checkbox label { display:inline-block; }
+
+.log_info { height:150px; overflow:auto; }
+
+.table td.type {
+ overflow:hidden;
+ position:relative;
+ text-align:center;
+ width:30px;
+}
+.type:before {
+ content:'';
+ display:block;
+ height:100%;
+ left:0;
+ position:absolute;
+ top:0;
+ width:5px;
+}
+.type.error:before {
+ background:#B94A48;
+}
+.type.log:before {
+ background:#B9D5E2;
+}
+.type.info:before {
+ background:#BED5BE;
+}
+
+.table th.span1 { text-align:center; }
\ No newline at end of file
diff --git a/log/css/test.css b/log/css/test.css
new file mode 100644
index 0000000..07baee2
--- /dev/null
+++ b/log/css/test.css
@@ -0,0 +1,3 @@
+ul { list-style:none; margin-left:5px; }
+li { margin:5px 0; }
+.checkbox label { display:inline-block; }
\ No newline at end of file
diff --git a/test/img/glyphicons-halflings-white.png b/log/img/glyphicons-halflings-white.png
similarity index 100%
rename from test/img/glyphicons-halflings-white.png
rename to log/img/glyphicons-halflings-white.png
diff --git a/test/img/glyphicons-halflings.png b/log/img/glyphicons-halflings.png
similarity index 100%
rename from test/img/glyphicons-halflings.png
rename to log/img/glyphicons-halflings.png
diff --git a/test/log.php b/log/index.php
similarity index 80%
rename from test/log.php
rename to log/index.php
index 05021a8..3fa2697 100644
--- a/test/log.php
+++ b/log/index.php
@@ -25,8 +25,10 @@ function stripslashes_deep($value) {
define('LOG_FILE', dirname(__FILE__) . '/log.txt');
define('ACCESS_FILE', dirname(__FILE__) . '/access.txt');
-// Only allow one entry per … seconds.
-define('THROTTLE_TIME', 60);
+// Only allow X entries per Y seconds. The default is to allow 15 entries
+// every 2 minutes.
+define('THROTTLE_COUNT', 15);
+define('THROTTLE_TIME', 120);
define('IP_ADDRESS', $_SERVER['REMOTE_ADDR']);
@@ -48,9 +50,6 @@ function stripslashes_deep($value) {
$format = $_REQUEST['format'];
}
-// Initially, assume that this client hasn't logged anything in the last minute.
-$has_accessed = false;
-
if ( !file_exists(ACCESS_FILE) ) {
if ( is_writable(dirname(ACCESS_FILE)) ) {
touch(ACCESS_FILE);
@@ -62,37 +61,45 @@ function stripslashes_deep($value) {
$accesses = file(ACCESS_FILE);
// Clear out old access logs.
+$access_count = 0;
$accesses = array_filter(
$accesses,
function($access) {
- global $has_accessed;
+ global $access_count;
+
+ if ( trim($access) == '' ) {
+ return false;
+ }
list($date, $ip) = explode("\t", $access);
$date = trim($date);
$ip = trim($ip);
- $fresh = ( strtotime($date) + THROTTLE_TIME > time() );
+ // Keep entries for twice as long as the throttle time…
+ $to_keep = ( strtotime($date) + ( THROTTLE_TIME * 2 ) > time() );
+ // …but only consider ones within the throttle time.
+ $to_consider = ( strtotime($date) + THROTTLE_TIME > time() );
// If the user has accessed within the last minute, proceed with the
// filtering — but we won't be logging anything later.
- if ( $fresh && $ip == IP_ADDRESS ) {
- $has_accessed = true;
+ if ( $to_consider && $ip == IP_ADDRESS ) {
+ $access_count++;
}
- return $fresh;
+ return $to_keep;
}
);
// Log an access from this IP.
-if ( !$has_accessed ) {
+if ( $access_count < THROTTLE_COUNT ) {
$accesses[] = date('Y-m-d H:i:s') . "\t" . IP_ADDRESS;
}
file_put_contents(ACCESS_FILE, join("\n", $accesses));
-// If the user has submitted a log in the last minute, bail out silently.
-if ( $has_accessed ) {
+// If the user has been throttled, bail out silently.
+if ( $access_count >= THROTTLE_COUNT ) {
die;
}
diff --git a/log/js/test.js b/log/js/test.js
new file mode 100644
index 0000000..bc23732
--- /dev/null
+++ b/log/js/test.js
@@ -0,0 +1,63 @@
+jQuery(function($) {
+ $('.no-js').removeClass('no-js');
+ $('iframe').height($(document).height());
+
+ var location = false;
+ var screen_size = false;
+ var user_agent = false;
+ var window_size = false;
+
+ $('input:button').click(function(e) {
+ e.preventDefault();
+ updateLogging();
+
+ if (!location && !screen_size && !user_agent && !window_size) {
+ if ($(this).hasClass('error')) {
+ $.error($(this).parents('li').find('input:text').val());
+ } else if ($(this).hasClass('info')) {
+ $.info($(this).parents('li').find('input:text').val());
+ } else if ($(this).hasClass('log')) {
+ $.log($(this).parents('li').find('input:text').val());
+ }
+ } else {
+ if ($(this).hasClass('error')) {
+ $.error({msg:$(this).parents('li').find('input:text').val()});
+ } else if ($(this).hasClass('info')) {
+ $.info({msg:$(this).parents('li').find('input:text').val()});
+ } else if ($(this).hasClass('log')) {
+ $.log({msg:$(this).parents('li').find('input:text').val()});
+ }
+ }
+
+ // Give the ajax request a chance to log this before refreshing the iframe.
+ setTimeout(function() {
+ // Refresh the iframe.
+ $('iframe').attr('src', $('iframe').attr('src'));
+ }, 500);
+ });
+
+ $('input:checkbox').click(function(e) {
+ // toggle our variables
+ if (this.name == 'location') {
+ location = !location;
+ } else if (this.name == 'screen_size') {
+ screen_size = !screen_size;
+ } else if (this.name == 'user_agent') {
+ user_agent = !user_agent;
+ } else if (this.name == 'window_size') {
+ window_size = !window_size;
+ }
+ });
+
+ function updateLogging() {
+ $.clientSideLogging({
+ log_level: 3,
+ client_info: {
+ location:location,
+ screen_size:screen_size,
+ user_agent:user_agent,
+ window_size:window_size
+ }
+ });
+ }
+});
\ No newline at end of file
diff --git a/log/js/view_logs.js b/log/js/view_logs.js
new file mode 100644
index 0000000..a27fc45
--- /dev/null
+++ b/log/js/view_logs.js
@@ -0,0 +1,13 @@
+jQuery(function($) {
+ $('.navbar input:text').keyup(function(e) {
+ var filter = new RegExp(this.value, 'gim');
+
+ $('tr').each(function(i, val) {
+ if ($(this).html().match(filter)) {
+ $(this).show();
+ } else {
+ $(this).hide();
+ }
+ });
+ });
+});
\ No newline at end of file
diff --git a/log/test.php b/log/test.php
new file mode 100644
index 0000000..274239d
--- /dev/null
+++ b/log/test.php
@@ -0,0 +1,77 @@
+
+
+
+
+
+