UNPKG

splunk-logging

Version:

Splunk HTTP Event Collector logging interface

307 lines (277 loc) 8.66 kB
/** * Utility functions. * @exports utils */ var utils = {}; /* * Copyright 2015 Splunk, Inc. * * 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. */ /* Utility Functions */ /** * Formats the time for Splunk Enterprise or Splunk Cloud as a the epoch time in seconds. * * @param {(string|number|date)} time - A date string, timestamp, or <code>Date</code> object. * @returns {number|null} Epoch time in seconds, or <code>null</code> if <code>time</code> is malformed. * @static */ utils.formatTime = function(time) { var cleanTime; // If time is a Date object, return its value. if (time instanceof Date) { time = time.valueOf(); } if (!time || time === null) { return null; } // Values with decimals if (time.toString().indexOf(".") !== -1) { cleanTime = parseFloat(time).toFixed(3); // Clean up the extra decimals right away. // A perfect time in milliseconds, with the decimal in the right spot. if (cleanTime.toString().indexOf(".") >= 10) { cleanTime = parseFloat(cleanTime.toString().substring(0, 14)).toFixed(3); } } // Values without decimals else { // A time in milliseconds, no decimal (ex: Date.now()). if (time.toString().length === 13) { cleanTime = (parseFloat(time) / 1000).toFixed(3); } // A time with fewer than expected digits. else if (time.toString().length <= 12) { cleanTime = parseFloat(time).toFixed(3); } // Any other value has more digits than the expected time format, get the first 14. else { cleanTime = parseFloat(time.toString().substring(0, 13)/1000).toFixed(3); } } return cleanTime; }; /** * Converts an iterable into to an array. * * @param {(Array|Object)} iterable - Thing to convert to an <code>Array</code>. * @returns {Array} * @static */ utils.toArray = function(iterable) { return Array.prototype.slice.call(iterable); }; // TODO: this isn't used anymore, remove it /** * Run async function in a chain, like {@link https://github.com/caolan/async#waterfall|Async.waterfall}. * * @param {(function[]|function)} tasks - <code>Array</code> of callback functions. * @param {function} [callback] - Final callback. * @static */ utils.chain = function(tasks, callback) { // Allow for just a list of functions if (arguments.length > 1 && typeof arguments[0] === "function") { var args = utils.toArray(arguments); tasks = args.slice(0, args.length - 1); callback = args[args.length - 1]; } tasks = tasks || []; callback = callback || function() {}; if (tasks.length === 0) { callback(); } else { var nextTask = function(task, remainingTasks, result) { var taskCallback = function(err) { if (err) { callback(err); } else { var args = utils.toArray(arguments); args.shift(); nextTask(remainingTasks[0], remainingTasks.slice(1), args); } }; var args = result; if (remainingTasks.length === 0) { args.push(callback); } else { args.push(taskCallback); } task.apply(null, args); }; nextTask(tasks[0], tasks.slice(1), []); } }; /** * Asynchronous while loop. * * @param {function} [condition] - A function returning a boolean, the loop condition. * @param {function} [body] - A function, the loop body. * @param {function} [callback] - Final callback. * @static */ utils.whilst = function (condition, body, callback) { condition = condition || function() { return false; }; body = body || function(done){ done(); }; callback = callback || function() {}; var wrappedCallback = function(err) { if (err) { callback(err); } else { utils.whilst(condition, body, callback); } }; if (condition()) { body(wrappedCallback); } else { callback(null); } }; /** * Waits using exponential backoff. * * @param {object} [opts] - Settings for this function. Expected keys: attempt, rand. * @param {function} [callback] - A callback function: <code>function(err, timeout)</code>. */ utils.expBackoff = function(opts, callback) { callback = callback || function(){}; if (!opts || typeof opts !== "object") { callback(new Error("Must send opts as an object.")); } else if (opts && !opts.hasOwnProperty("attempt")) { callback(new Error("Must set opts.attempt.")); } else { var min = 10; var max = 1000 * 60 * 2; // 2 minutes is a reasonable max delay var rand = Math.random(); if (opts.hasOwnProperty("rand")) { rand = opts.rand; } rand++; var timeout = Math.round(rand * min * Math.pow(2, opts.attempt)); timeout = Math.min(timeout, max); setTimeout( function() { callback(null, timeout); }, timeout ); } }; /** * Binds a function to an instance of an object. * * @param {object} [self] - An object to bind the <code>fn</code> function parameter to. * @param {object} [fn] - A function to bind to the <code>self</code> argument. * @returns {function} * @static */ utils.bind = function(self, fn) { return function () { return fn.apply(self, arguments); }; }; /** * Copies all properties into a new object which is returned. * * @param {object} [obj] - Object to copy properties from. */ utils.copyObject = function(obj) { var ret = {}; for (var key in obj) { if (obj.hasOwnProperty(key)) { ret[key] = obj[key]; } } return ret; }; /** * Copies all elements into a new array, which is returned. * * @param {array} [arr] - Array to copy elements from. * @returns {array} * @static */ utils.copyArray = function(arr) { var ret = []; for (var i = 0; arr && i < arr.length; i++) { ret[i] = arr[i]; } return ret; }; /** * Takes a property name, then any number of objects as arguments * and performs logical OR operations on them one at a time * Returns true as soon as a truthy * value is found, else returning false. * * @param {string} [prop] - property name for other arguments. * @returns {boolean} * @static */ utils.orByProp = function(prop) { var ret = false; for (var i = 1; !ret && i < arguments.length; i++) { if (arguments[i]) { ret = ret || arguments[i][prop]; } } return ret; }; /** * Like <code>utils.orByProp()</code> but for a falsey property. * The first argument after <code>prop</code> with that property * defined will be returned. * Useful for Booleans and numbers. * * @param {string} [prop] - property name for other arguments. * @returns {boolean} * @static */ utils.orByFalseyProp = function(prop) { var ret = null; // Logic is reversed here, first value wins for (var i = arguments.length - 1; i > 0; i--) { if (arguments[i] && arguments[i].hasOwnProperty(prop)) { ret = arguments[i][prop]; } } return ret; }; /** * Tries to validate the <code>value</code> parameter as a non-negative * integer. * * @param {number} [value] - Some value, expected to be a positive integer. * @param {number} [label] - Human readable name for <code>value</code> * for error messages. * @returns {number} * @throws Will throw an error if the <code>value</code> parameter cannot by parsed as an integer. * @static */ utils.validateNonNegativeInt = function(value, label) { value = parseInt(value, 10); if (isNaN(value)) { throw new Error(label + " must be a number, found: " + value); } else if (value < 0) { throw new Error(label + " must be a positive number, found: " + value); } return value; }; module.exports = utils;