http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/39e89ea0/htrace-core/src/web/lib/rome-2.1.0/rome.standalone.min.js ---------------------------------------------------------------------- diff --git a/htrace-core/src/web/lib/rome-2.1.0/rome.standalone.min.js b/htrace-core/src/web/lib/rome-2.1.0/rome.standalone.min.js deleted file mode 100644 index 541911f..0000000 --- a/htrace-core/src/web/lib/rome-2.1.0/rome.standalone.min.js +++ /dev/null @@ -1,2 +0,0 @@ -// [email protected], MIT licensed. https://github.com/bevacqua/rome -!function(t){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var e;"undefined"!=typeof window?e=window:"undefined"!=typeof global?e=global:"undefined"!=typeof self&&(e=self),e.rome=t()}}(function(){return function t(e,n,r){function o(i,s){if(!n[i]){if(!e[i]){var u="function"==typeof require&&require;if(!s&&u)return u(i,!0);if(a)return a(i,!0);throw new Error("Cannot find module '"+i+"'")}var c=n[i]={exports:{}};e[i][0].call(c.exports,function(t){var n=e[i][1][t];return o(n?n:t)},c,c.exports,t,e,n,r)}return n[i].exports}for(var a="function"==typeof require&&require,i=0;i<r.length;i++)o(r[i]);return o}({1:[function(t,e){function n(){}var r=e.exports={};r.nextTick=function(){var t="undefined"!=typeof window&&window.setImmediate,e="undefined"!=typeof window&&window.postMessage&&window.addEventListener;if(t)return function(t){return window.setImmediate(t)};if(e){var n=[];return window.addEventListe ner("message",function(t){var e=t.source;if((e===window||null===e)&&"process-tick"===t.data&&(t.stopPropagation(),n.length>0)){var r=n.shift();r()}},!0),function(t){n.push(t),window.postMessage("process-tick","*")}}return function(t){setTimeout(t,0)}}(),r.title="browser",r.browser=!0,r.env={},r.argv=[],r.on=n,r.addListener=n,r.once=n,r.off=n,r.removeListener=n,r.removeAllListeners=n,r.emit=n,r.binding=function(){throw new Error("process.binding is not supported")},r.cwd=function(){return"/"},r.chdir=function(){throw new Error("process.chdir is not supported")}},{}],2:[function(t,e){e.exports=t("./src/contra.emitter.js")},{"./src/contra.emitter.js":3}],3:[function(t,e){(function(t){!function(n,r){"use strict";function o(t,e){return Array.prototype.slice.call(t,e)}function a(t,e,n){t&&s(function(){t.apply(n||null,e||[])})}function i(t,e){var n=e||{},i={};return t===r&&(t={}),t.on=function(e,n){return i[e]?i[e].push(n):i[e]=[n],t},t.once=function(e,n){return n._once=!0,t.on(e,n),t},t.o ff=function(e,n){var r=arguments.length;if(1===r)delete i[e];else if(0===r)i={};else{var o=i[e];if(!o)return t;o.splice(o.indexOf(n),1)}return t},t.emit=function(){var e=o(arguments);return t.emitterSnapshot(e.shift()).apply(this,e)},t.emitterSnapshot=function(e){var r=(i[e]||[]).slice(0);return function(){var s=o(arguments),u=this||t;if("error"===e&&n.throws!==!1&&!r.length)throw 1===s.length?s[0]:s;return i[e]=r.filter(function(t){return n.async?a(t,s,u):t.apply(u,s),!t._once}),t}},t}var s,u=""+r,c="function"==typeof setImmediate;s=c?function(t){setImmediate(t)}:typeof t!==u&&t.nextTick?t.nextTick:function(t){setTimeout(t,0)},typeof e!==u&&e.exports?e.exports=i:(n.contra=n.contra||{},n.contra.emitter=i)}(this)}).call(this,t("FWaASH"))},{FWaASH:1}],4:[function(t,e){for(var n=t("performance-now"),r="undefined"==typeof window?{}:window,o=["moz","webkit"],a="AnimationFrame",i=r["request"+a],s=r["cancel"+a]||r["cancelRequest"+a],u=!0,c=0;c<o.length&&!i;c++)i=r[o[c]+"Request"+a],s=r[o[c ]+"Cancel"+a]||r[o[c]+"CancelRequest"+a];if(!i||!s){u=!1;var l=0,d=0,f=[],m=1e3/60;i=function(t){if(0===f.length){var e=n(),r=Math.max(0,m-(e-l));l=r+e,setTimeout(function(){var t=f.slice(0);f.length=0;for(var e=0;e<t.length;e++)if(!t[e].cancelled)try{t[e].callback(l)}catch(n){setTimeout(function(){throw n},0)}},Math.round(r))}return f.push({handle:++d,callback:t,cancelled:!1}),d},s=function(t){for(var e=0;e<f.length;e++)f[e].handle===t&&(f[e].cancelled=!0)}}e.exports=function(t){return u?i.call(r,function(){try{t.apply(this,arguments)}catch(e){setTimeout(function(){throw e},0)}}):i.call(r,t)},e.exports.cancel=function(){s.apply(r,arguments)}},{"performance-now":5}],5:[function(t,e){(function(t){(function(){var n,r,o;"undefined"!=typeof performance&&null!==performance&&performance.now?e.exports=function(){return performance.now()}:"undefined"!=typeof t&&null!==t&&t.hrtime?(e.exports=function(){return(n()-o)/1e6},r=t.hrtime,n=function(){var t;return t=r(),1e9*t[0]+t[1]},o=n()):Date.n ow?(e.exports=function(){return Date.now()-o},o=Date.now()):(e.exports=function(){return(new Date).getTime()-o},o=(new Date).getTime())}).call(this)}).call(this,t("FWaASH"))},{FWaASH:1}],6:[function(t,e){"use strict";function n(t,e){var n=u[t.id];return n&&n[e.id]}function r(t,e){var n=u[t.id];n||(n=u[t.id]={});var r=a(e);n[e.id]=r,t.on("data",r),t.on("destroyed",o.bind(null,t,e))}function o(t,e){var n=u[t.id];if(n){var r=n[e.id];t.off("data",r),delete n[e.id]}}function a(t){return function(){t.refresh()}}function i(t,e){s(e.associated)||n(t,e)||r(t,e)}var s=t("./isInput"),u={};e.exports={add:i,remove:o}},{"./isInput":17}],7:[function(t,e){"use strict";function n(t){function e(){return Te}function n(n){return de=l(n||t,Te),ye||(ye=i({className:de.styles.container})),pe=de.weekdayFormat,he=pe.length,we=r,ve=r,be=r,ge=r,de.appendTo.appendChild(ye),J(ye),De=!1,fe=de.initialValue?de.initialValue:d.moment(),me=fe.clone(),Te.container=ye,Te.destroyed=!1,Te.destroy=h.bind(Te,!1),Te.emitVal ues=z,Te.getDate=ue,Te.getDateString=ce,Te.getMoment=le,Te.hide=O,Te.options=w,Te.options.reset=b,Te.refresh=$,Te.restore=e,Te.setValue=G,Te.show=N,N(),v(),p(),Te}function p(){Te.emit("ready",c(de))}function h(t){ye&&ye.parentNode.removeChild(ye),de&&v(!0);var r=Te.emitterSnapshot("destroyed");return Te.destroyed=!0,Te.destroy=e,Te.emitValues=e,Te.getDate=y,Te.getDateString=y,Te.getMoment=y,Te.hide=e,Te.options=e,Te.options.reset=e,Te.refresh=e,Te.restore=n,Te.setValue=e,Te.show=e,Te.off(),t!==!0&&r(),Te}function v(t){var e=t?"remove":"add";de.autoHideOnBlur&&m[e](document.documentElement,"focus",H,!0),de.autoHideOnClick&&m[e](document,"click",S)}function w(t){return 0===arguments.length?c(de):(h(),n(t),Te)}function b(){return w({appendTo:de.appendTo})}function g(){De||(De=!0,x(),k(),Te.emit("render"))}function x(){function t(t){var e=i({className:de.styles.month,parent:xe});0===t&&(ke=i({type:"button",className:de.styles.back,attributes:{type:"button"},parent:e})),t===de.monthsInCa lendar-1&&(Ee=i({type:"button",className:de.styles.next,attributes:{type:"button"},parent:e}));var n,r=i({className:de.styles.monthLabel,parent:e}),o=i({type:"table",className:de.styles.dayTable,parent:e}),a=i({type:"thead",className:de.styles.dayHead,parent:o}),s=i({type:"tr",className:de.styles.dayRow,parent:a}),u=i({type:"tbody",className:de.styles.dayBody,parent:o});for(n=0;he>n;n++)i({type:"th",className:de.styles.dayHeadElem,parent:s,text:pe[E(n)]});u.setAttribute(Me,t),Ce.push({label:r,body:u})}if(de.date){var e;for(Ce=[],xe=i({className:de.styles.date,parent:ye}),e=0;e<de.monthsInCalendar;e++)t(e);m.add(ke,"click",V),m.add(Ee,"click",B),m.add(xe,"click",re)}}function k(){if(de.time&&de.timeInterval){var t=i({className:de.styles.time,parent:ye});Fe=i({className:de.styles.selectedTime,parent:t,text:fe.format(de.timeFormat)}),m.add(Fe,"click",A),Ae=i({className:de.styles.timeList,parent:t}),m.add(Ae,"click",se);for(var e=d.moment("00:00:00","HH:mm:ss"),n=e.clone().add(1,"days") ;e.isBefore(n);)i({className:de.styles.timeOption,parent:Ae,text:e.format(de.timeFormat)}),e.add(de.timeInterval,"seconds")}}function E(t,e){var n=e?-1:1,r=t+de.weekStart*n;return(r>=he||0>r)&&(r+=he*-n),r}function F(){if(de.time&&De){var t,e,n,r,o=Ae.children,a=o.length;for(r=0;a>r;r++)n=o[r],e=d.moment(s(n),de.timeFormat),t=ie(fe.clone(),e),n.style.display=U(t,!1,de.timeValidator)?"block":"none"}}function A(t){var e="boolean"==typeof t?t:"none"===Ae.style.display;e?T():D()}function T(){Ae&&(Ae.style.display="block")}function D(){Ae&&(Ae.style.display="none")}function M(){ye.style.display="inline-block",Te.emit("show")}function C(){ye.style.display="none",Te.emit("hide")}function N(){return g(),$(),A(!de.date),M(),Te}function O(){return D(),a(C),Te}function I(){D();var t=f.contains(ye,de.styles.positioned);return t&&a(C),Te}function j(t){var e=t.target;if(e===Te.associated)return!0;for(;e;){if(e===ye)return!0;e=e.parentNode}}function H(t){j(t)||I()}function S(t){j(t)||I()}function V(){L("subtract")}function B(){L("add")}function L(t){var e,n="add"===t?-1:1,r=de.monthsInCalendar+n*ae(ge);me[t](r,"months"),e=te(me.clone()),fe=e||fe,e&&(me=e.clone()),q()}function q(t){P(),W(),t!==!0&&z(),F()}function P(){function t(t,e){var n=me.clone().add(e,"months");s(t.label,n.format(de.monthFormat)),J(t.body)}if(de.date&&De){var e=me.year(),n=me.month(),r=me.date();if(r!==be||n!==we||e!==ve){var o=R();if(be=me.date(),we=me.month(),ve=me.year(),o)return Y(),void 0;Ce.forEach(t),K()}}}function Y(){function t(t){var e,n=[];for(e=0;e<t.length;e++)n.push(t[e]);return n}function e(e){return t(e.children)}function n(t){return!f.contains(t,de.styles.dayPrevMonth)&&!f.contains(t,de.styles.dayNextMonth)}var r=me.date()-1;oe(!1),Ce.forEach(function(o){var a;_(o.date,me)&&(a=t(o.body.children).map(e),a=Array.prototype.concat.apply([],a).filter(n),oe(a[r]))})}function R(){function t(t){return ve?_(t.date,me):!1}return Ce.some(t)}function _(t,e){return t&&e&&t.year()===e.year()&&t.month( )===e.month()}function W(){de.time&&De&&s(Fe,fe.format(de.timeFormat))}function z(){return Te.emit("data",ce()),Te.emit("year",fe.year()),Te.emit("month",fe.month()),Te.emit("day",fe.day()),Te.emit("time",fe.format(de.timeFormat)),Te}function $(){return ve=!1,we=!1,be=!1,q(!0),Te}function G(t){var e=u(t,de.inputFormat);if(null!==e)return fe=te(e)||fe,me=fe.clone(),q(!0),Te}function J(t,e){for(;t&&t.firstChild;)t.removeChild(t.firstChild);e===!0&&t.parentNode.removeChild(t)}function K(){var t;for(t=0;t<de.monthsInCalendar;t++)Q(t)}function Q(t){function e(t){var e,r,o;for(e=0;e<t.length;e++)f.children.length===he&&(f=i({type:"tr",className:de.styles.dayRow,parent:a.body})),r=t.base.clone().add(e,"days"),o=i({type:"td",parent:f,text:r.format(de.dayFormat),className:n(r,t.cell.join(" ").split(" ")).join(" ")}),t.selectable&&r.date()===c&&oe(o)}function n(t,e){return U(t,!0,de.dateValidator)||e.push(p),e}function r(t,e){return t&&e.push(de.styles.dayConcealed),e}var o,a=Ce[t],s=me.clone ().add(t,"months"),u=s.daysInMonth(),c=s.month()!==fe.month()?-1:fe.date(),l=s.clone().date(1),d=E(l.day(),!0),f=i({type:"tr",className:de.styles.dayRow,parent:a.body}),m=r(0!==t,[de.styles.dayBodyElem,de.styles.dayPrevMonth]),y=r(t!==de.monthsInCalendar-1,[de.styles.dayBodyElem,de.styles.dayNextMonth]),p=de.styles.dayDisabled;e({base:l.clone().subtract(d,"days"),length:d,cell:m}),e({base:l.clone(),length:u,cell:[de.styles.dayBodyElem],selectable:!0}),o=l.clone().add(u,"days"),e({base:o,length:he-f.children.length,cell:y}),ke.disabled=!X(l,!0),Ee.disabled=!Z(o,!0),a.date=s.clone()}function U(t,e,n){if(!X(t,e))return!1;if(!Z(t,e))return!1;var r=(n||Function.prototype).call(Te,t.toDate());return r!==!1}function X(t,e){var n=de.min?e?de.min.clone().startOf("day"):de.min:!1;return!n||!t.isBefore(n)}function Z(t,e){var n=de.max?e?de.max.clone().endOf("day"):de.max:!1;return!n||!t.isAfter(n)}function te(t){if(de.min&&t.isBefore(de.min))return te(de.min.clone());if(de.max&&t.isAfter(de.max ))return te(de.max.clone());var e=t.clone().subtract(1,"days");return ne(e,t,"add")?ee(e):(e=t.clone(),ne(e,t,"subtract")?ee(e):void 0)}function ee(t){var e,n=t.clone().subtract(de.timeInterval,"seconds"),r=Math.ceil(Ne/de.timeInterval);for(e=0;r>e;e++)if(n.add(de.timeInterval,"seconds"),n.date()>t.date()&&n.subtract(1,"days"),de.timeValidator.call(Te,n.toDate())!==!1)return n}function ne(t,e,n){for(var r=!1;r===!1&&(t[n](1,"days"),t.month()===e.month());)r=de.dateValidator.call(Te,t.toDate());return r!==!1}function re(t){var e=t.target;if(!f.contains(e,de.styles.dayDisabled)&&f.contains(e,de.styles.dayBodyElem)){var n=parseInt(s(e),10),r=f.contains(e,de.styles.dayPrevMonth),o=f.contains(e,de.styles.dayNextMonth),a=ae(e)-ae(ge);fe.add(a,"months"),(r||o)&&fe.add(r?-1:1,"months"),oe(e),fe.date(n),ie(fe,te(fe)||fe),me=fe.clone(),de.autoClose===!0&&I(),q()}}function oe(t){ge&&f.remove(ge,de.styles.selectedDay),t&&f.add(t,de.styles.selectedDay),ge=t}function ae(t){for(var e;t&&t.getAttri bute;){if(e=t.getAttribute(Me),"string"==typeof e)return parseInt(e,10);t=t.parentNode}return 0}function ie(t,e){return t.hour(e.hour()).minute(e.minute()).second(e.second()),t}function se(t){var e=t.target;if(f.contains(e,de.styles.timeOption)){var n=d.moment(s(e),de.timeFormat);ie(fe,n),me=fe.clone(),z(),W(),!de.date&&de.autoClose===!0||"time"===de.autoClose?I():D()}}function ue(){return fe.toDate()}function ce(t){return fe.format(t||de.inputFormat)}function le(){return fe.clone()}var de,fe,me,ye,pe,he,ve,we,be,ge,xe,ke,Ee,Fe,Ae,Te=o({}),De=!1,Me="data-rome-offset",Ce=[],Ne=86400;return n(),a(p),Te}var r,o=t("contra.emitter"),a=t("raf"),i=t("./dom"),s=t("./text"),u=t("./parse"),c=t("./clone"),l=t("./defaults"),d=t("./momentum"),f=t("./classes"),m=t("./events"),y=t("./noop");e.exports=n},{"./classes":8,"./clone":9,"./defaults":11,"./dom":12,"./events":13,"./momentum":18,"./noop":19,"./parse":20,"./text":32,"contra.emitter":2,raf:4}],8:[function(t,e){"use strict";function n(t){retur n t.className.replace(s,"").split(u)}function r(t,e){t.className=e.join(" ")}function o(t,e){var n=a(t,e);n.push(e),r(t,n)}function a(t,e){var o=n(t),a=o.indexOf(e);return-1!==a&&(o.splice(a,1),r(t,o)),o}function i(t,e){return-1!==n(t).indexOf(e)}var s=/^\s+|\s+$/g,u=/\s+/;e.exports={add:o,remove:a,contains:i}},{}],9:[function(t,e){"use strict";function n(t){var e,o={};for(var a in t)e=t[a],o[a]=e?r.isMoment(e)?e.clone():e._isStylesConfiguration?n(e):e:e;return o}var r=t("./momentum");e.exports=n},{"./momentum":18}],10:[function(t,e){"use strict";function n(t,e){var n,s=r.find(t);return s?s:(n=i(t)?o(t,e):a(t,e),n.associated=t,r.assign(t,n),n)}var r=t("./index"),o=t("./input"),a=t("./inline"),i=t("./isInput");e.exports=n},{"./index":14,"./inline":15,"./input":16,"./isInput":17}],11:[function(t,e){"use strict";function n(t,e){var n,i,s=t||{};if(s.autoHideOnClick===i&&(s.autoHideOnClick=!0),s.autoHideOnBlur===i&&(s.autoHideOnBlur=!0),s.autoClose===i&&(s.autoClose=!0),s.appendTo===i&&( s.appendTo=document.body),"parent"===s.appendTo){if(!o(e.associated))throw new Error("Inline calendars must be appended to a parent node explicitly.");s.appendTo=e.associated.parentNode}if(s.invalidate===i&&(s.invalidate=!0),s.required===i&&(s.required=!1),s.date===i&&(s.date=!0),s.time===i&&(s.time=!0),s.date===!1&&s.time===!1)throw new Error("At least one of `date` or `time` must be `true`.");if(s.inputFormat===i&&(s.inputFormat=s.date&&s.time?"YYYY-MM-DD HH:mm":s.date?"YYYY-MM-DD":"HH:mm"),s.initialValue=s.initialValue===i?null:r(s.initialValue,s.inputFormat),s.min=s.min===i?null:r(s.min,s.inputFormat),s.max=s.max===i?null:r(s.max,s.inputFormat),s.timeInterval===i&&(s.timeInterval=1800),s.min&&s.max)if(s.max.isBefore(s.min)&&(n=s.max,s.max=s.min,s.min=n),s.date===!0){if(s.max.clone().subtract(1,"days").isBefore(s.min))throw new Error("`max` must be at least one day after `min`")}else if(1e3*s.timeInterval-s.min%(1e3*s.timeInterval)>s.max-s.min)throw new Error("`min` to `max` rang e must allow for at least one time option that matches `timeInterval`");if(s.dateValidator===i&&(s.dateValidator=Function.prototype),s.timeValidator===i&&(s.timeValidator=Function.prototype),s.timeFormat===i&&(s.timeFormat="HH:mm"),s.weekStart===i&&(s.weekStart=a.moment().weekday(0).day()),s.weekdayFormat===i&&(s.weekdayFormat="min"),"long"===s.weekdayFormat)s.weekdayFormat=a.moment.weekdays();else if("short"===s.weekdayFormat)s.weekdayFormat=a.moment.weekdaysShort();else if("min"===s.weekdayFormat)s.weekdayFormat=a.moment.weekdaysMin();else if(!Array.isArray(s.weekdayFormat)||s.weekdayFormat.length<7)throw new Error("`weekdays` must be `min`, `short`, or `long`");s.monthsInCalendar===i&&(s.monthsInCalendar=1),s.monthFormat===i&&(s.monthFormat="MMMM YYYY"),s.dayFormat===i&&(s.dayFormat="DD"),s.styles===i&&(s.styles={}),s.styles._isStylesConfiguration=!0;var u=s.styles;return u.back===i&&(u.back="rd-back"),u.container===i&&(u.container="rd-container"),u.positioned===i&&(u.positioned= "rd-container-attachment"),u.date===i&&(u.date="rd-date"),u.dayBody===i&&(u.dayBody="rd-days-body"),u.dayBodyElem===i&&(u.dayBodyElem="rd-day-body"),u.dayPrevMonth===i&&(u.dayPrevMonth="rd-day-prev-month"),u.dayNextMonth===i&&(u.dayNextMonth="rd-day-next-month"),u.dayDisabled===i&&(u.dayDisabled="rd-day-disabled"),u.dayConcealed===i&&(u.dayConcealed="rd-day-concealed"),u.dayHead===i&&(u.dayHead="rd-days-head"),u.dayHeadElem===i&&(u.dayHeadElem="rd-day-head"),u.dayRow===i&&(u.dayRow="rd-days-row"),u.dayTable===i&&(u.dayTable="rd-days"),u.month===i&&(u.month="rd-month"),u.monthLabel===i&&(u.monthLabel="rd-month-label"),u.next===i&&(u.next="rd-next"),u.selectedDay===i&&(u.selectedDay="rd-day-selected"),u.selectedTime===i&&(u.selectedTime="rd-time-selected"),u.time===i&&(u.time="rd-time"),u.timeList===i&&(u.timeList="rd-time-list"),u.timeOption===i&&(u.timeOption="rd-time-option"),s}var r=t("./parse"),o=t("./isInput"),a=t("./momentum");e.exports=n},{"./isInput":17,"./momentum":18,"./par se":20}],12:[function(t,e){"use strict";function n(t){var e=t||{};e.type||(e.type="div");var n=document.createElement(e.type);return e.className&&(n.className=e.className),e.text&&(n.innerText=n.textContent=e.text),e.attributes&&Object.keys(e.attributes).forEach(function(t){n.setAttribute(t,e.attributes[t])}),e.parent&&e.parent.appendChild(n),n}e.exports=n},{}],13:[function(t,e){"use strict";function n(t,e,n,r){return t.addEventListener(e,n,r)}function r(t,e,n,r){return t.attachEvent("on"+e,function(e){var r=e||window.event;r.target=r.target||r.srcElement,r.preventDefault=r.preventDefault||function(){r.returnValue=!1},r.stopPropagation=r.stopPropagation||function(){r.cancelBubble=!0},n.call(t,r)},r)}function o(t,e,n){return t.removeEventListener(e,n)}function a(t,e,n){return t.detachEvent("on"+e,n)}var i=n,s=o;window.addEventListener||(i=r),window.removeEventListener||(s=a),e.exports={add:i,remove:s}},{}],14:[function(t,e){"use strict";function n(t){if("number"!=typeof t&&t&&t.getAt tribute)return n(t.getAttribute(a));var e=i[t];return e!==o?e:null}function r(t,e){t.setAttribute(a,e.id=i.push(e)-1)}var o,a="data-rome-id",i=[];e.exports={find:n,assign:r}},{}],15:[function(t,e){"use strict";function n(t,e){var n=e||{};return n.appendTo=t,r(n)}var r=(t("raf"),t("./calendar"));e.exports=n},{"./calendar":7,raf:4}],16:[function(t,e){"use strict";function n(t,e){function n(n){x=a(n||e,F),u.add(F.container,x.styles.positioned),c.add(F.container,"mousedown",m),c.add(F.container,"click",f),F.getDate=g(F.getDate),F.getDateString=g(F.getDateString),F.getMoment=g(F.getMoment),x.initialValue&&(t.value=x.initialValue.format(x.inputFormat)),F.on("data",w),F.on("show",T),d(),A(),F.hide()}function l(){d(!0)}function d(e){var r=e?"remove":"add";c[r](t,"click",p),c[r](t,"touchend",p),c[r](t,"focusin",p),c[r](t,"change",A),c[r](t,"keypress",A),c[r](t,"keydown",A),c[r](t,"input",A),x.invalidate&&c[r](t,"blur",y),c[r](window,"resize",T),e?(F.once("ready",n),F.off("destroyed",l)):(F.o ff("ready",n),F.once("destroyed",l))}function f(){E=!0,t.focus(),E=!1}function m(){function t(){k=!1}k=!0,o(t)}function y(){k||b()||F.emitValues()}function p(){E||F.show()}function h(){var e=t.getBoundingClientRect(),n=document.body.scrollTop||document.documentElement.scrollTop;F.container.style.top=e.top+n+t.offsetHeight+"px",F.container.style.left=e.left+"px"}function v(){var e=t.value.trim();if(!b()){var n=s.moment(e,x.inputFormat,x.strictParse);F.setValue(n)}}function w(e){t.value=e}function b(){return x.required===!1&&""===t.value.trim()}function g(t){return function(){return b()?null:t.apply(this,arguments)}}var x,k,E,F=i(e),A=r(v,30),T=r(h,30);return n(e),F}var r=t("./throttle"),o=t("raf"),a=(t("./clone"),t("./defaults")),i=t("./calendar"),s=t("./momentum"),u=t("./classes"),c=t("./events");e.exports=n},{"./calendar":7,"./classes":8,"./clone":9,"./defaults":11,"./events":13,"./momentum":18,"./throttle":33,raf:4}],17:[function(t,e){"use strict";function n(t){return t&&t.nodeNam e&&"input"===t.nodeName.toLowerCase()}e.exports=n},{}],18:[function(t,e){"use strict";function n(t){return t&&Object.prototype.hasOwnProperty.call(t,"_isAMomentObject")}var r={moment:null,isMoment:n};e.exports=r},{}],19:[function(t,e){"use strict";function n(){}e.exports=n},{}],20:[function(t,e){"use strict";function n(t,e){return"string"==typeof t?o.moment(t,e):"[object Date]"===Object.prototype.toString.call(t)?o.moment(t):o.isMoment(t)?t.clone():void 0}function r(t,e){var r=n(t,"string"==typeof e?e:null);return r&&r.isValid()?r:null}var o=t("./momentum");e.exports=r},{"./momentum":18}],21:[function(){"use strict";Array.prototype.filter||(Array.prototype.filter=function(t,e){var n=[];return this.forEach(function(r,o,a){t.call(e,r,o,a)&&n.push(r)},e),n})},{}],22:[function(){"use strict";Array.prototype.forEach||(Array.prototype.forEach=function(t,e){if(void 0===this||null===this||"function"!=typeof t)throw new TypeError;for(var n=this,r=n.length,o=0;r>o;o++)o in n&&t.call(e,n[o],o, n)})},{}],23:[function(){"use strict";Array.prototype.indexOf||(Array.prototype.indexOf=function(t,e){if(void 0===this||null===this)throw new TypeError;var n=this.length;for(e=+e||0,1/0===Math.abs(e)?e=0:0>e&&(e+=n,0>e&&(e=0));n>e;e++)if(this[e]===t)return e;return-1})},{}],24:[function(){"use strict";Array.isArray||(Array.isArray=function(t){return""+t!==t&&"[object Array]"===Object.prototype.toString.call(t)})},{}],25:[function(){"use strict";Array.prototype.map||(Array.prototype.map=function(t,e){var n,r,o;if(null==this)throw new TypeError("this is null or not defined");var a=Object(this),i=a.length>>>0;if("function"!=typeof t)throw new TypeError(t+" is not a function");for(arguments.length>1&&(n=e),r=new Array(i),o=0;i>o;)o in a&&(r[o]=t.call(n,a[o],o,a)),o++;return r})},{}],26:[function(){"use strict";Array.prototype.some||(Array.prototype.some=function(t,e){var n,r;if(null==this)throw new TypeError("this is null or not defined");var o=Object(this),a=o.length>>>0;if("function"! =typeof t)throw new TypeError(t+" is not a function");for(arguments.length>1&&(n=e),r=0;a>r;){if(r in o){var i=t.call(n,o[r],r,o);if(i)return!0}r++}return!1})},{}],27:[function(){"use strict";Function.prototype.bind||(Function.prototype.bind=function(t){if("function"!=typeof this)throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");var e=Array.prototype.slice.call(arguments,1),n=this,r=function(){},o=function(){var o=this instanceof r&&t?this:t,a=e.concat(Array.prototype.slice.call(arguments));return n.apply(o,a)};return r.prototype=this.prototype,o.prototype=new r,o})},{}],28:[function(){"use strict";var t=Object.prototype.hasOwnProperty,e=!{toString:null}.propertyIsEnumerable("toString"),n=["toString","toLocaleString","valueOf","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","constructor"],r=n.length;Object.keys||(Object.keys=function(o){if("object"!=typeof o&&("function"!=typeof o||null===o))throw new TypeError("Object.keys called on non-object");var a,i,s=[];for(a in o)t.call(o,a)&&s.push(a);if(e)for(i=0;r>i;i++)t.call(o,n[i])&&s.push(n[i]);return s})},{}],29:[function(){"use strict";String.prototype.trim||(String.prototype.trim=function(){return this.replace(/^\s+|\s+$/g,"")})},{}],30:[function(t,e){"use strict";t("./polyfills/function.bind"),t("./polyfills/array.foreach"),t("./polyfills/array.map"),t("./polyfills/array.filter"),t("./polyfills/array.isarray"),t("./polyfills/array.indexof"),t("./polyfills/array.some"),t("./polyfills/string.trim"),t("./polyfills/object.keys");var n=t("./core"),r=t("./index"),o=t("./use");n.use=o.bind(n),n.find=r.find,n.val=t("./validators"),e.exports=n},{"./core":10,"./index":14,"./polyfills/array.filter":21,"./polyfills/array.foreach":22,"./polyfills/array.indexof":23,"./polyfills/array.isarray":24,"./polyfills/array.map":25,"./polyfills/array.some":26,"./polyfills/function.bind":27,"./polyfills/object.keys":28,"./polyfills/string.trim":29,"./use":34,"./validators":35}],31:[fu nction(t,e){(function(n){var r=t("./rome"),o=t("./momentum");if(r.use(n.moment),void 0===o.moment)throw new Error("rome depends on moment.js, you can get it at http://momentjs.com, or you could use the bundled distribution file instead.");e.exports=r}).call(this,"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"./momentum":18,"./rome":30}],32:[function(t,e){"use strict";function n(t,e){return 2===arguments.length&&(t.innerText=t.textContent=e),t.innerText||t.textContent}e.exports=n},{}],33:[function(t,e){"use strict";e.exports=function(t,e){var n,r=-1/0;return function(){function o(){clearTimeout(n),n=null;var a=r+e,i=Date.now();i>a?(r=i,t.apply(this,arguments)):n=setTimeout(o,a-i)}n||o()}}},{}],34:[function(t,e){"use strict";function n(t){this.moment=r.moment=t}var r=t("./momentum");e.exports=n},{"./momentum":18}],35:[function(t,e){"use strict";function n(t){return function(e){var n=a(e);return function(r){var s=o.find(e),u=a(r),c=n||s&&s.getMoment();return c?( s&&i.add(this,s),t(u,c)):!0}}}function r(t,e){return function(n,r){function s(t){var e,n,r=o.find(t);return r?e=n=r.getMoment():Array.isArray(t)?(e=t[0],n=t[1]):e=n=t,r&&i.add(r,this),{start:a(e).startOf("day").toDate(),end:a(n).endOf("day").toDate()}}var u,c=arguments.length;return Array.isArray(n)?u=n:1===c?u=[n]:2===c&&(u=[[n,r]]),function(n){return u.map(s.bind(this))[t](e.bind(this,n))}}}var o=t("./index"),a=t("./parse"),i=t("./association"),s=n(function(t,e){return t>=e}),u=n(function(t,e){return t>e}),c=n(function(t,e){return e>=t}),l=n(function(t,e){return e>t}),d=r("every",function(t,e){return e.start>t||e.end<t}),f=r("some",function(t,e){return e.start<=t&&e.end>=t});e.exports={afterEq:s,after:u,beforeEq:c,before:l,except:d,only:f}},{"./association":6,"./index":14,"./parse":20}]},{},[31])(31)}); \ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/39e89ea0/htrace-htraced/pom.xml ---------------------------------------------------------------------- diff --git a/htrace-htraced/pom.xml b/htrace-htraced/pom.xml index 6995ed4..0e951ad 100644 --- a/htrace-htraced/pom.xml +++ b/htrace-htraced/pom.xml @@ -99,6 +99,53 @@ language governing permissions and limitations under the License. --> </execution> </executions> </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-antrun-plugin</artifactId> + <executions> + <execution> + <phase>compile</phase> + <id>go_compile</id> + <goals><goal>run</goal></goals> + <configuration> + <tasks> + <exec executable="./gobuild.sh" + dir="${basedir}/src/go/" + failonerror="true"> + <env key="RELEASE_VERSION" value="${project.version}"/> + </exec> + </tasks> + </configuration> + </execution> + <execution> + <phase>test</phase> + <id>go_test</id> + <goals><goal>run</goal></goals> + <configuration> + <tasks> + <exec executable="bash" failonerror="true" dir="${basedir}/src/go"> + <arg value="-c"/> + <arg value="[ x$SKIPTESTS = xtrue ] || ./gobuild.sh test"/> + <env key="SKIPTESTS" value="${skipTests}"/> + </exec> + </tasks> + </configuration> + </execution> + <execution> + <phase>clean</phase> + <id>go_clean</id> + <goals><goal>run</goal></goals> + <configuration> + <tasks> + <exec executable="bash" failonerror="true" dir="${basedir}/src/go"> + <arg value="-c"/> + <arg value="./gobuild.sh clean"/> + </exec> + </tasks> + </configuration> + </execution> + </executions> + </plugin> </plugins> </build> <dependencies> http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/39e89ea0/htrace-htraced/src/go/BUILDING.txt ---------------------------------------------------------------------- diff --git a/htrace-htraced/src/go/BUILDING.txt b/htrace-htraced/src/go/BUILDING.txt new file mode 100644 index 0000000..d54d410 --- /dev/null +++ b/htrace-htraced/src/go/BUILDING.txt @@ -0,0 +1,30 @@ +Building the HTrace Go code +=========================== +The htrace go code consists of 4 main parts: +* The "htraced" standalone server + This is a server which accepts trace spans, and services REST queries. + +* The "htrace" command-line program which can query the server + This is a simple command-line program which can query the htrace server. + +* The htraced Javascript Web UI (not yet implemented) + +* The htrace go client library (not yet implemented) + This is the equivalent of the Java HTrace client library, but written in Go. + +You can build all these parts simply by running "gobuild.sh". +The binaries will be created in bin/. + +Dependencies +============ +You will need to install: +* The Go programming language +* The development package for leveldb (some Linux distros call this "leveldb-devel") containing libleveldb.so + +htraced requires libleveldb.so to be in your shared library path in order to run. +You can set LD_LIBRARY_PATH to the path for this library, or simply install +libleveldb.so to your system library path. + +Testing +======= +You can run the unit tests by running "./gobuild.sh test" http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/39e89ea0/htrace-htraced/src/go/Godeps/Godeps.json ---------------------------------------------------------------------- diff --git a/htrace-htraced/src/go/Godeps/Godeps.json b/htrace-htraced/src/go/Godeps/Godeps.json new file mode 100644 index 0000000..10c8e5d --- /dev/null +++ b/htrace-htraced/src/go/Godeps/Godeps.json @@ -0,0 +1,26 @@ +{ + "ImportPath": "git-wip-us.apache.org/repos/asf/incubator-htrace.git", + "GoVersion": "go1.3.1", + "Deps": [ + { + "ImportPath": "github.com/alecthomas/kingpin", + "Rev": "afafa8aab106d31c9dc8f5e562b3f30f6246c3d4" + }, + { + "ImportPath": "github.com/alecthomas/units", + "Rev": "6b4e7dc5e3143b85ea77909c72caf89416fc2915" + }, + { + "ImportPath": "github.com/gorilla/context", + "Rev": "215affda49addc4c8ef7e2534915df2c8c35c6cd" + }, + { + "ImportPath": "github.com/gorilla/mux", + "Rev": "e444e69cbd2e2e3e0749a2f3c717cec491552bbf" + }, + { + "ImportPath": "github.com/jmhodges/levigo", + "Rev": "2c43dde93d0e056173706534afd514fcbc1dd578" + } + ] +} http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/39e89ea0/htrace-htraced/src/go/format.sh ---------------------------------------------------------------------- diff --git a/htrace-htraced/src/go/format.sh b/htrace-htraced/src/go/format.sh new file mode 100755 index 0000000..46aa5b1 --- /dev/null +++ b/htrace-htraced/src/go/format.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +# + +# +# Reformats the HTrace code. +# +# ./format.sh Reformats all code. +# + +die() { + echo $@ + exit 1 +} + +# Check for gofmt. It should be installed whenever the go developement tools +# are installed. +which gofmt &> /dev/null +[ $? -ne 0 ] && die "You must install the gofmt code reformatting formatting tool." + +# Find go sources. We assume no newlines or whitespace in file names. +SCRIPT_DIR="$(cd "$( dirname $0 )" && pwd)" +find "${SCRIPT_DIR}/src" -noleaf -xdev -name '*.go' | xargs -L 1 gofmt -w http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/39e89ea0/htrace-htraced/src/go/gobuild.sh ---------------------------------------------------------------------- diff --git a/htrace-htraced/src/go/gobuild.sh b/htrace-htraced/src/go/gobuild.sh new file mode 100755 index 0000000..1a4e5f1 --- /dev/null +++ b/htrace-htraced/src/go/gobuild.sh @@ -0,0 +1,111 @@ +#!/usr/bin/env bash + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +# + +# +# Builds the HTrace server code. +# +# ./build.sh Builds the code. +# ./build.sh test Builds and runs all unit tests. +# ./build.sh bench Builds and runs all benchmarks +# + +die() { + echo $@ + exit 1 +} + +ACTION=install +if [ $# -gt 0 ]; then + ACTION="${1}" + shift +fi +RELEASE_VERSION=${RELEASE_VERSION:-unknown} + +# Set up directories. The build/ directory is where build dependencies and +# build binaries should go. +SCRIPT_DIR="$(cd "$( dirname $0 )" && pwd)" +export GOBIN="${SCRIPT_DIR}/build" +mkdir -p "${GOBIN}" || die "failed to mkdir -p ${GOBIN}" +cd "${GOBIN}" || die "failed to cd to ${SCRIPT_DIR}" +export GOPATH="${GOBIN}:${SCRIPT_DIR}" + +# Check for go +which go &> /dev/null +if [ $? -ne 0 ]; then + cat <<EOF +You must install the Golang programming language. + +If you are using Debian, try "apt-get install golang". +For Red Hat, try "yum install go". +For other distributions and operating systems use your packaging tool. +EOF +exit 1 +fi + +# Check for libleveldb.so +if [ -x "/sbin/ldconfig" ]; then + # Suse requires ldconfig to be run via the absolute path + ldconfig=/sbin/ldconfig +else + which ldconfig &> /dev/null + [ $? -eq 0 ] && ldconfig=ldconfig +fi +if [ -n "${ldconfig}" ]; then + if "${ldconfig}" -p | grep -q libleveldb; then + : + else + echo "You must install the leveldb-devel package (or distro-specific equivalent.)" + exit 1 + fi +fi + +case $ACTION in +clean) + rm -rf -- "${GOBIN}" ${SCRIPT_DIR}/pkg + ;; +install) + # Ensure that we have the godep program. + PATH="${PATH}:${GOBIN}" + which godep &> /dev/null + if [ $? -ne 0 ]; then + echo "Installing godep..." + go get github.com/tools/godep || die "failed to get godep" + fi + + # Download dependencies into the build directory. + echo "godep restore..." + godep restore || die "failed to set up dependencies" + + # Discover the git version + GIT_VERSION=$(git rev-parse HEAD) + [ $? -eq 0 ] || GIT_VERSION="unknown" + + # Inject the release and git version into the htraced ldflags. + FLAGS="-X main.RELEASE_VERSION ${RELEASE_VERSION} -X main.GIT_VERSION ${GIT_VERSION}" + go install -ldflags "${FLAGS}" -v org/apache/htrace/... "$@" + ;; +bench) + go test org/apache/htrace/... -test.bench=. "$@" + ;; +*) + go ${ACTION} org/apache/htrace/... "$@" + ;; +esac http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/39e89ea0/htrace-htraced/src/go/src/org/apache/htrace/client/client.go ---------------------------------------------------------------------- diff --git a/htrace-htraced/src/go/src/org/apache/htrace/client/client.go b/htrace-htraced/src/go/src/org/apache/htrace/client/client.go new file mode 100644 index 0000000..44e2f69 --- /dev/null +++ b/htrace-htraced/src/go/src/org/apache/htrace/client/client.go @@ -0,0 +1,244 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +package client + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io" + "io/ioutil" + "net/http" + "org/apache/htrace/common" + "org/apache/htrace/conf" +) + +// A golang client for htraced. +// TODO: fancier APIs for streaming spans in the background, optimize TCP stuff + +func NewClient(cnf *conf.Config) (*Client, error) { + hcl := Client{} + hcl.restAddr = cnf.Get(conf.HTRACE_WEB_ADDRESS) + if cnf.Get(conf.HTRACE_HRPC_ADDRESS) != "" { + var err error + hcl.hcr, err = newHClient(cnf) + if err != nil { + return nil, err + } + } + return &hcl, nil +} + +type Client struct { + // REST address of the htraced server. + restAddr string + + // The HRPC client, or null if it is not enabled. + hcr *hClient +} + +// Get the htraced server information. +func (hcl *Client) GetServerInfo() (*common.ServerInfo, error) { + buf, _, err := hcl.makeGetRequest("server/info") + if err != nil { + return nil, err + } + var info common.ServerInfo + err = json.Unmarshal(buf, &info) + if err != nil { + return nil, errors.New(fmt.Sprintf("Error: error unmarshalling response "+ + "body %s: %s", string(buf), err.Error())) + } + return &info, nil +} + +// Get information about a trace span. Returns nil, nil if the span was not found. +func (hcl *Client) FindSpan(sid common.SpanId) (*common.Span, error) { + buf, rc, err := hcl.makeGetRequest(fmt.Sprintf("span/%016x", uint64(sid))) + if err != nil { + if rc == http.StatusNoContent { + return nil, nil + } + return nil, err + } + var span common.Span + err = json.Unmarshal(buf, &span) + if err != nil { + return nil, errors.New(fmt.Sprintf("Error unmarshalling response "+ + "body %s: %s", string(buf), err.Error())) + } + return &span, nil +} + +func (hcl *Client) WriteSpans(req *common.WriteSpansReq) error { + if hcl.hcr != nil { + return hcl.hcr.writeSpans(req) + } else { + return hcl.writeSpansHttp(req) + } +} + +func (hcl *Client) writeSpansHttp(req *common.WriteSpansReq) error { + var w bytes.Buffer + var err error + for i := range req.Spans { + var buf []byte + buf, err = json.Marshal(req.Spans[i]) + if err != nil { + return errors.New(fmt.Sprintf("Error serializing span: %s", + err.Error())) + } + _, err = w.Write(buf) + if err != nil { + return errors.New(fmt.Sprintf("Error writing span: %s", + err.Error())) + } + _, err = w.Write([]byte{'\n'}) + //err = io.WriteString(&w, "\n") + if err != nil { + return errors.New(fmt.Sprintf("Error writing: %s", + err.Error())) + } + } + customHeaders := make(map[string]string) + if req.DefaultPid != "" { + customHeaders["htrace-pid"] = req.DefaultPid + } + _, _, err = hcl.makeRestRequest("POST", "writeSpans", + &w, customHeaders) + if err != nil { + return err + } + return nil +} + +// Find the child IDs of a given span ID. +func (hcl *Client) FindChildren(sid common.SpanId, lim int) ([]common.SpanId, error) { + buf, _, err := hcl.makeGetRequest(fmt.Sprintf("span/%016x/children?lim=%d", + uint64(sid), lim)) + if err != nil { + return nil, err + } + var spanIds []common.SpanId + err = json.Unmarshal(buf, &spanIds) + if err != nil { + return nil, errors.New(fmt.Sprintf("Error: error unmarshalling response "+ + "body %s: %s", string(buf), err.Error())) + } + return spanIds, nil +} + +// Make a query +func (hcl *Client) Query(query *common.Query) ([]common.Span, error) { + in, err := json.Marshal(query) + if err != nil { + return nil, errors.New(fmt.Sprintf("Error marshalling query: %s", err.Error())) + } + var out []byte + var url = fmt.Sprintf("query?query=%s", in) + out, _, err = hcl.makeGetRequest(url) + if err != nil { + return nil, err + } + var spans []common.Span + err = json.Unmarshal(out, &spans) + if err != nil { + return nil, errors.New(fmt.Sprintf("Error unmarshalling results: %s", err.Error())) + } + return spans, nil +} + +var EMPTY = make(map[string]string) + +func (hcl *Client) makeGetRequest(reqName string) ([]byte, int, error) { + return hcl.makeRestRequest("GET", reqName, nil, EMPTY) +} + +// Make a general JSON REST request. +// Returns the request body, the response code, and the error. +// Note: if the response code is non-zero, the error will also be non-zero. +func (hcl *Client) makeRestRequest(reqType string, reqName string, reqBody io.Reader, + customHeaders map[string]string) ([]byte, int, error) { + url := fmt.Sprintf("http://%s/%s", + hcl.restAddr, reqName) + req, err := http.NewRequest(reqType, url, reqBody) + req.Header.Set("Content-Type", "application/json") + for k, v := range customHeaders { + req.Header.Set(k, v) + } + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + return nil, -1, errors.New(fmt.Sprintf("Error: error making http request to %s: %s\n", url, + err.Error())) + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + return nil, resp.StatusCode, + errors.New(fmt.Sprintf("Error: got bad response status from %s: %s\n", url, resp.Status)) + } + var body []byte + body, err = ioutil.ReadAll(resp.Body) + if err != nil { + return nil, -1, errors.New(fmt.Sprintf("Error: error reading response body: %s\n", err.Error())) + } + return body, 0, nil +} + +// Dump all spans from the htraced daemon. +func (hcl *Client) DumpAll(lim int, out chan *common.Span) error { + defer func() { + close(out) + }() + searchId := common.SpanId(0) + for { + q := common.Query{ + Lim: lim, + Predicates: []common.Predicate{ + common.Predicate{ + Op: "ge", + Field: "spanid", + Val: searchId.String(), + }, + }, + } + spans, err := hcl.Query(&q) + if err != nil { + return errors.New(fmt.Sprintf("Error querying spans with IDs at or after "+ + "%s: %s", searchId.String(), err.Error())) + } + if len(spans) == 0 { + return nil + } + for i := range spans { + out <- &spans[i] + } + searchId = spans[len(spans)-1].Id + 1 + } +} + +func (hcl *Client) Close() { + if hcl.hcr != nil { + hcl.hcr.Close() + } + hcl.restAddr = "" + hcl.hcr = nil +} http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/39e89ea0/htrace-htraced/src/go/src/org/apache/htrace/client/hclient.go ---------------------------------------------------------------------- diff --git a/htrace-htraced/src/go/src/org/apache/htrace/client/hclient.go b/htrace-htraced/src/go/src/org/apache/htrace/client/hclient.go new file mode 100644 index 0000000..1730c02 --- /dev/null +++ b/htrace-htraced/src/go/src/org/apache/htrace/client/hclient.go @@ -0,0 +1,151 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +package client + +import ( + "encoding/binary" + "encoding/json" + "errors" + "fmt" + "io" + "net" + "net/rpc" + "org/apache/htrace/common" + "org/apache/htrace/conf" +) + +type hClient struct { + rpcClient *rpc.Client +} + +type HrpcClientCodec struct { + rwc io.ReadWriteCloser + length uint32 +} + +func (cdc *HrpcClientCodec) WriteRequest(req *rpc.Request, msg interface{}) error { + methodId := common.HrpcMethodNameToId(req.ServiceMethod) + if methodId == common.METHOD_ID_NONE { + return errors.New(fmt.Sprintf("HrpcClientCodec: Unknown method name %s", + req.ServiceMethod)) + } + buf, err := json.Marshal(msg) + if err != nil { + return errors.New(fmt.Sprintf("HrpcClientCodec: Unable to marshal "+ + "message as JSON: %s", err.Error())) + } + if len(buf) > common.MAX_HRPC_BODY_LENGTH { + return errors.New(fmt.Sprintf("HrpcClientCodec: message body is %d "+ + "bytes, but the maximum message size is %d bytes.", + len(buf), common.MAX_HRPC_BODY_LENGTH)) + } + hdr := common.HrpcRequestHeader{ + Magic: common.HRPC_MAGIC, + MethodId: methodId, + Seq: req.Seq, + Length: uint32(len(buf)), + } + err = binary.Write(cdc.rwc, binary.BigEndian, &hdr) + if err != nil { + return errors.New(fmt.Sprintf("Error writing header bytes: %s", + err.Error())) + } + _, err = cdc.rwc.Write(buf) + if err != nil { + return errors.New(fmt.Sprintf("Error writing body bytes: %s", + err.Error())) + } + return nil +} + +func (cdc *HrpcClientCodec) ReadResponseHeader(resp *rpc.Response) error { + hdr := common.HrpcResponseHeader{} + err := binary.Read(cdc.rwc, binary.BigEndian, &hdr) + if err != nil { + return errors.New(fmt.Sprintf("Error reading response header "+ + "bytes: %s", err.Error())) + } + resp.ServiceMethod = common.HrpcMethodIdToMethodName(hdr.MethodId) + if resp.ServiceMethod == "" { + return errors.New(fmt.Sprintf("Error reading response header: "+ + "invalid method ID %d.", hdr.MethodId)) + } + resp.Seq = hdr.Seq + if hdr.ErrLength > 0 { + if hdr.ErrLength > common.MAX_HRPC_ERROR_LENGTH { + return errors.New(fmt.Sprintf("Error reading response header: "+ + "error message was %d bytes long, but "+ + "MAX_HRPC_ERROR_LENGTH is %d.", + hdr.ErrLength, common.MAX_HRPC_ERROR_LENGTH)) + } + buf := make([]byte, hdr.ErrLength) + var nread int + nread, err = cdc.rwc.Read(buf) + if uint32(nread) != hdr.ErrLength { + return errors.New(fmt.Sprintf("Error reading response header: "+ + "failed to read %d bytes of error message.", nread)) + } + if err != nil { + return errors.New(fmt.Sprintf("Error reading response header: "+ + "failed to read %d bytes of error message: %s", + nread, err.Error())) + } + resp.Error = string(buf) + } else { + resp.Error = "" + } + cdc.length = hdr.Length + return nil +} + +func (cdc *HrpcClientCodec) ReadResponseBody(body interface{}) error { + dec := json.NewDecoder(io.LimitReader(cdc.rwc, int64(cdc.length))) + err := dec.Decode(body) + if err != nil { + return errors.New(fmt.Sprintf("Failed to read response body: %s", + err.Error())) + } + return nil +} + +func (cdc *HrpcClientCodec) Close() error { + return cdc.rwc.Close() +} + +func newHClient(cnf *conf.Config) (*hClient, error) { + hcr := hClient{} + addr := cnf.Get(conf.HTRACE_HRPC_ADDRESS) + conn, err := net.Dial("tcp", addr) + if err != nil { + return nil, errors.New(fmt.Sprintf("Error contacting the HRPC server "+ + "at %s: %s", addr, err.Error())) + } + hcr.rpcClient = rpc.NewClientWithCodec(&HrpcClientCodec{rwc: conn}) + return &hcr, nil +} + +func (hcr *hClient) writeSpans(req *common.WriteSpansReq) error { + resp := common.WriteSpansResp{} + return hcr.rpcClient.Call(common.METHOD_NAME_WRITE_SPANS, req, &resp) +} + +func (hcr *hClient) Close() { + hcr.rpcClient.Close() +} http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/39e89ea0/htrace-htraced/src/go/src/org/apache/htrace/common/log.go ---------------------------------------------------------------------- diff --git a/htrace-htraced/src/go/src/org/apache/htrace/common/log.go b/htrace-htraced/src/go/src/org/apache/htrace/common/log.go new file mode 100644 index 0000000..c5f495d --- /dev/null +++ b/htrace-htraced/src/go/src/org/apache/htrace/common/log.go @@ -0,0 +1,268 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +package common + +import ( + "errors" + "fmt" + "org/apache/htrace/conf" + "os" + "path/filepath" + "sort" + "strings" + "sync" + "time" +) + +// A logSink is a place logs can be written to. +type logSink struct { + path logPath + file *os.File + lock sync.Mutex + refCount int // protected by logFilesLock +} + +// Write to the logSink. +func (sink *logSink) write(str string) { + sink.lock.Lock() + defer sink.lock.Unlock() + _, err := sink.file.Write([]byte(str)) + if err != nil { + fmt.Fprintf(os.Stderr, "Error logging to '%s': %s\n", sink.path, err.Error()) + } +} + +// Unreference the logSink. If there are no more references, and the logSink is +// closeable, then we will close it here. +func (sink *logSink) Unref() { + logFilesLock.Lock() + defer logFilesLock.Unlock() + sink.refCount-- + if sink.refCount <= 0 { + if sink.path.IsCloseable() { + err := sink.file.Close() + if err != nil { + fmt.Fprintf(os.Stderr, "Error closing log file %s: %s\n", + sink.path, err.Error()) + } + } + logSinks[sink.path] = nil + } +} + +type logPath string + +// An empty LogPath represents "stdout." +const STDOUT_LOG_PATH = "" + +// Convert a path to a logPath. +func logPathFromString(path string) logPath { + if path == STDOUT_LOG_PATH { + return logPath("") + } + absPath, err := filepath.Abs(path) + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to get absolute path of %s: %s\n", + path, err.Error()) + return logPath(path) + } + return logPath(absPath) +} + +// Convert the path to a human-readable string. +func (path logPath) String() string { + if path == "" { + return "(stdout)" + } else { + return string(path) + } +} + +// Return true if the path is closeable. stdout is not closeable. +func (path logPath) IsCloseable() bool { + return path != STDOUT_LOG_PATH +} + +func (path logPath) Open() *logSink { + if path == STDOUT_LOG_PATH { + return &logSink{path: path, file: os.Stdout} + } + file, err := os.OpenFile(string(path), os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0777) + if err != nil { + sink := &logSink{path: STDOUT_LOG_PATH, file: os.Stdout} + fmt.Fprintf(os.Stderr, "Failed to open log file %s: %s\n", + path, err.Error()) + return sink + } + return &logSink{path: path, file: file} +} + +var logFilesLock sync.Mutex + +var logSinks map[logPath]*logSink = make(map[logPath]*logSink) + +func getOrCreateLogSink(pathStr string) *logSink { + path := logPathFromString(pathStr) + logFilesLock.Lock() + defer logFilesLock.Unlock() + sink := logSinks[path] + if sink == nil { + sink = path.Open() + logSinks[path] = sink + } + sink.refCount++ + return sink +} + +type Level int + +const ( + TRACE Level = iota + DEBUG + INFO + WARN + ERROR +) + +var levelToString map[Level]string = map[Level]string{ + TRACE: "TRACE", + DEBUG: "DEBUG", + INFO: "INFO", + WARN: "WARN", + ERROR: "ERROR", +} + +func (level Level) String() string { + return levelToString[level] +} + +func (level Level) LogString() string { + return level.String()[0:1] +} + +func LevelFromString(str string) (Level, error) { + for k, v := range levelToString { + if strings.ToLower(v) == strings.ToLower(str) { + return k, nil + } + } + var levelNames sort.StringSlice + levelNames = make([]string, len(levelToString)) + var i int + for _, v := range levelToString { + levelNames[i] = v + i++ + } + sort.Sort(levelNames) + return TRACE, errors.New(fmt.Sprintf("No such level as '%s'. Valid "+ + "levels are '%v'\n", str, levelNames)) +} + +type Logger struct { + sink *logSink + Level Level +} + +func NewLogger(faculty string, cnf *conf.Config) *Logger { + path, level := parseConf(faculty, cnf) + sink := getOrCreateLogSink(path) + return &Logger{sink: sink, Level: level} +} + +func parseConf(faculty string, cnf *conf.Config) (string, Level) { + facultyLogPathKey := faculty + "." + conf.HTRACE_LOG_PATH + var facultyLogPath string + if cnf.Contains(facultyLogPathKey) { + facultyLogPath = cnf.Get(facultyLogPathKey) + } else { + facultyLogPath = cnf.Get(conf.HTRACE_LOG_PATH) + } + facultyLogLevelKey := faculty + conf.HTRACE_LOG_LEVEL + var facultyLogLevelStr string + if cnf.Contains(facultyLogLevelKey) { + facultyLogLevelStr = cnf.Get(facultyLogLevelKey) + } else { + facultyLogLevelStr = cnf.Get(conf.HTRACE_LOG_LEVEL) + } + level, err := LevelFromString(facultyLogLevelStr) + if err != nil { + fmt.Fprintf(os.Stderr, "Error configuring log level: %s. Using TRACE.\n") + level = TRACE + } + return facultyLogPath, level +} + +func (lg *Logger) Trace(str string) { + lg.write(TRACE, str) +} + +func (lg *Logger) Tracef(format string, v ...interface{}) { + lg.write(TRACE, fmt.Sprintf(format, v...)) +} + +func (lg *Logger) Debug(str string) { + lg.write(DEBUG, str) +} + +func (lg *Logger) Debugf(format string, v ...interface{}) { + lg.write(DEBUG, fmt.Sprintf(format, v...)) +} + +func (lg *Logger) Info(str string) { + lg.write(INFO, str) +} + +func (lg *Logger) Infof(format string, v ...interface{}) { + lg.write(INFO, fmt.Sprintf(format, v...)) +} + +func (lg *Logger) Warn(str string) error { + lg.write(WARN, str) + return errors.New(str) +} + +func (lg *Logger) Warnf(format string, v ...interface{}) error { + str := fmt.Sprintf(format, v...) + lg.write(WARN, str) + return errors.New(str) +} + +func (lg *Logger) Error(str string) error { + lg.write(ERROR, str) + return errors.New(str) +} + +func (lg *Logger) Errorf(format string, v ...interface{}) error { + str := fmt.Sprintf(format, v...) + lg.write(ERROR, str) + return errors.New(str) +} + +func (lg *Logger) write(level Level, str string) { + if level >= lg.Level { + lg.sink.write(time.Now().Format(time.RFC3339) + " " + + level.LogString() + ": " + str) + } +} + +func (lg *Logger) Close() { + lg.sink.Unref() + lg.sink = nil +} http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/39e89ea0/htrace-htraced/src/go/src/org/apache/htrace/common/log_test.go ---------------------------------------------------------------------- diff --git a/htrace-htraced/src/go/src/org/apache/htrace/common/log_test.go b/htrace-htraced/src/go/src/org/apache/htrace/common/log_test.go new file mode 100644 index 0000000..b415ce2 --- /dev/null +++ b/htrace-htraced/src/go/src/org/apache/htrace/common/log_test.go @@ -0,0 +1,122 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +package common + +import ( + "bufio" + "fmt" + "io" + "io/ioutil" + "org/apache/htrace/conf" + "os" + "strings" + "testing" +) + +func newLogger(faculty string, args ...string) *Logger { + cnfBld := conf.Builder{Defaults: conf.DEFAULTS} + cnf, err := cnfBld.Build() + if err != nil { + panic(fmt.Sprintf("failed to create conf: %s", err.Error())) + } + cnf2 := cnf.Clone(args...) + lg := NewLogger(faculty, cnf2) + return lg +} + +func TestNewLogger(t *testing.T) { + lg := newLogger("foo", "log.level", "TRACE") + lg.Close() +} + +func verifyLines(t *testing.T, rdr io.Reader, lines []string) { + scanner := bufio.NewScanner(rdr) + lineIdx := 0 + for scanner.Scan() { + line := scanner.Text() + if !strings.Contains(line, lines[lineIdx]) { + t.Fatalf("Error on line %d: didn't find substring '%s' in line '%s'\n", + (lineIdx + 1), lines[lineIdx], line) + } + lineIdx++ + } + if err := scanner.Err(); err != nil { + t.Fatal(err.Error()) + } +} + +func TestFileLogs(t *testing.T) { + tempDir, err := ioutil.TempDir(os.TempDir(), "testFileLogs") + if err != nil { + panic(fmt.Sprintf("error creating tempdir: %s\n", err.Error())) + } + defer os.RemoveAll(tempDir) + logPath := tempDir + conf.PATH_SEP + "log" + lg := newLogger("foo", "log.level", "DEBUG", + "foo.log.level", "INFO", + "log.path", logPath) + lg.Tracef("Non-important stuff, ignore this.\n") + lg.Infof("problem with the foobar\n") + lg.Tracef("More non-important stuff, also ignore this.\n") + lg.Infof("and another problem with the foobar\n") + logFile, err := os.Open(logPath) + if err != nil { + t.Fatalf("failed to open file %s: %s\n", logPath, err.Error()) + } + verifyLines(t, logFile, []string{ + "problem with the foobar", + "and another problem with the foobar", + }) + logFile.Close() + lg.Close() +} + +func TestMultipleFileLogs(t *testing.T) { + tempDir, err := ioutil.TempDir(os.TempDir(), "testMultipleFileLogs") + if err != nil { + panic(fmt.Sprintf("error creating tempdir: %s\n", err.Error())) + } + defer os.RemoveAll(tempDir) + logPath := tempDir + conf.PATH_SEP + "log" + fooLg := newLogger("foo", "log.level", "DEBUG", + "foo.log.level", "INFO", + "log.path", logPath) + fooLg.Infof("The foo needs maintenance.\n") + barLg := newLogger("bar", "log.level", "DEBUG", + "foo.log.level", "INFO", + "log.path", logPath) + barLg.Debugf("The bar is open\n") + fooLg.Errorf("Fizz buzz\n") + logFile, err := os.Open(logPath) + if err != nil { + t.Fatalf("failed to open file %s: %s\n", logPath, err.Error()) + } + fooLg.Tracef("Fizz buzz2\n") + barLg.Tracef("Fizz buzz3\n") + verifyLines(t, logFile, []string{ + "The foo needs maintenance.", + "The bar is open", + "Fizz buzz", + "Fizz buzz3", + }) + logFile.Close() + fooLg.Close() + barLg.Close() +} http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/39e89ea0/htrace-htraced/src/go/src/org/apache/htrace/common/process.go ---------------------------------------------------------------------- diff --git a/htrace-htraced/src/go/src/org/apache/htrace/common/process.go b/htrace-htraced/src/go/src/org/apache/htrace/common/process.go new file mode 100644 index 0000000..d138178 --- /dev/null +++ b/htrace-htraced/src/go/src/org/apache/htrace/common/process.go @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +package common + +import ( + "bufio" + "org/apache/htrace/conf" + "os" + "os/signal" + "syscall" +) + +func LoadApplicationConfig() *conf.Config { + cnf, dlog := conf.LoadApplicationConfig() + lg := NewLogger("conf", cnf) + defer lg.Close() + if lg.Level <= DEBUG { + // Print out the debug information from loading the configuration. + scanner := bufio.NewScanner(dlog) + for scanner.Scan() { + lg.Debugf(scanner.Text() + "\n") + } + } + return cnf +} + +func InstallSignalHandlers(cnf *conf.Config) { + fatalSigs := []os.Signal{ + os.Interrupt, + os.Kill, + syscall.SIGINT, + syscall.SIGABRT, + syscall.SIGALRM, + syscall.SIGBUS, + syscall.SIGFPE, + syscall.SIGILL, + syscall.SIGQUIT, + syscall.SIGSEGV, + syscall.SIGTERM, + } + sigChan := make(chan os.Signal, len(fatalSigs)) + signal.Notify(sigChan, fatalSigs...) + lg := NewLogger("exit", cnf) + go func() { + sig := <-sigChan + lg.Errorf("Terminating on signal: %v\n", sig) + lg.Close() + os.Exit(1) + }() +} http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/39e89ea0/htrace-htraced/src/go/src/org/apache/htrace/common/query.go ---------------------------------------------------------------------- diff --git a/htrace-htraced/src/go/src/org/apache/htrace/common/query.go b/htrace-htraced/src/go/src/org/apache/htrace/common/query.go new file mode 100644 index 0000000..a32909e --- /dev/null +++ b/htrace-htraced/src/go/src/org/apache/htrace/common/query.go @@ -0,0 +1,117 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +package common + +import ( + "encoding/json" +) + +// +// Represents queries that can be sent to htraced. +// +// Each query consists of set of predicates that will be 'AND'ed together to +// return a set of spans. Predicates contain an operation, a field, and a +// value. +// +// For example, a query might be "return the first 100 spans between 5:00pm +// and 5:01pm" This query would have two predicates: time greater than or +// equal to 5:00pm, and time less than or equal to 5:01pm. +// In HTrace, times are always expressed in milliseconds since the Epoch. +// So this would become: +// { "lim" : 100, "pred" : [ +// { "op" : "ge", "field" : "begin", "val" : 1234 }, +// { "op" : "le", "field" : "begin", "val" : 5678 }, +// ] } +// +// Where '1234' and '5678' were replaced by times since the epoch in +// milliseconds. +// + +type Op string + +const ( + CONTAINS Op = "cn" + EQUALS Op = "eq" + LESS_THAN_OR_EQUALS Op = "le" + GREATER_THAN_OR_EQUALS Op = "ge" + GREATER_THAN Op = "gt" +) + +func (op Op) IsDescending() bool { + return op == LESS_THAN_OR_EQUALS +} + +func (op Op) IsValid() bool { + ops := ValidOps() + for i := range ops { + if ops[i] == op { + return true + } + } + return false +} + +func ValidOps() []Op { + return []Op{CONTAINS, EQUALS, LESS_THAN_OR_EQUALS, GREATER_THAN_OR_EQUALS, + GREATER_THAN} +} + +type Field string + +const ( + SPAN_ID Field = "spanid" + DESCRIPTION Field = "description" + BEGIN_TIME Field = "begin" + END_TIME Field = "end" + DURATION Field = "duration" +) + +func (field Field) IsValid() bool { + fields := ValidFields() + for i := range fields { + if fields[i] == field { + return true + } + } + return false +} + +func ValidFields() []Field { + return []Field{SPAN_ID, DESCRIPTION, BEGIN_TIME, END_TIME, DURATION} +} + +type Predicate struct { + Op Op `json:"op"` + Field Field `json:"field"` + Val string `val:"val"` +} + +type Query struct { + Predicates []Predicate `json:"pred"` + Lim int `json:"lim"` +} + +func (query *Query) String() string { + buf, err := json.Marshal(query) + if err != nil { + panic(err) + } + return string(buf) +} http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/39e89ea0/htrace-htraced/src/go/src/org/apache/htrace/common/query_test.go ---------------------------------------------------------------------- diff --git a/htrace-htraced/src/go/src/org/apache/htrace/common/query_test.go b/htrace-htraced/src/go/src/org/apache/htrace/common/query_test.go new file mode 100644 index 0000000..2697d9c --- /dev/null +++ b/htrace-htraced/src/go/src/org/apache/htrace/common/query_test.go @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +package common + +import ( + "testing" +) + +func TestValidOps(t *testing.T) { + for i := range ValidOps() { + op := ValidOps()[i] + if !op.IsValid() { + t.Fatalf("op %s was in ValidOps, but IsValid returned false.\n", op) + } + } + invalidOp := Op("completelybogus") + if invalidOp.IsValid() { + t.Fatalf("op %s was invalid, but IsValid returned true.\n", invalidOp) + } +} + +func TestValidFields(t *testing.T) { + for i := range ValidFields() { + field := ValidFields()[i] + if !field.IsValid() { + t.Fatalf("field %s was in ValidFields, but IsValid returned false.\n", field) + } + } + invalidField := Field("completelybogus") + if invalidField.IsValid() { + t.Fatalf("field %s was invalid, but IsValid returned true.\n", invalidField) + } +} http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/39e89ea0/htrace-htraced/src/go/src/org/apache/htrace/common/rest.go ---------------------------------------------------------------------- diff --git a/htrace-htraced/src/go/src/org/apache/htrace/common/rest.go b/htrace-htraced/src/go/src/org/apache/htrace/common/rest.go new file mode 100644 index 0000000..eeb9568 --- /dev/null +++ b/htrace-htraced/src/go/src/org/apache/htrace/common/rest.go @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +package common + +// Info returned by /serverInfo +type ServerInfo struct { + // The server release version. + ReleaseVersion string + + // The git hash that this software was built with. + GitVersion string +} http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/39e89ea0/htrace-htraced/src/go/src/org/apache/htrace/common/rpc.go ---------------------------------------------------------------------- diff --git a/htrace-htraced/src/go/src/org/apache/htrace/common/rpc.go b/htrace-htraced/src/go/src/org/apache/htrace/common/rpc.go new file mode 100644 index 0000000..cdf7e08 --- /dev/null +++ b/htrace-htraced/src/go/src/org/apache/htrace/common/rpc.go @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +package common + +// The 4-byte magic number which is sent first in the HRPC header +const HRPC_MAGIC = 0x48545243 + +// Method ID codes. Do not reorder these. +const ( + METHOD_ID_NONE = 0 + METHOD_ID_WRITE_SPANS = iota +) + +const METHOD_NAME_WRITE_SPANS = "HrpcHandler.WriteSpans" + +// Maximum length of the error message passed in an HRPC response +const MAX_HRPC_ERROR_LENGTH = 4 * 1024 * 1024 + +// Maximum length of HRPC message body +const MAX_HRPC_BODY_LENGTH = 64 * 1024 * 1024 + +// A request to write spans to htraced. +type WriteSpansReq struct { + DefaultPid string + Spans []*Span +} + +// A response to a WriteSpansReq +type WriteSpansResp struct { +} + +// The header which is sent over the wire for HRPC +type HrpcRequestHeader struct { + Magic uint32 + MethodId uint32 + Seq uint64 + Length uint32 +} + +// The response which is sent over the wire for HRPC +type HrpcResponseHeader struct { + Seq uint64 + MethodId uint32 + ErrLength uint32 + Length uint32 +} + +func HrpcMethodIdToMethodName(id uint32) string { + switch id { + case METHOD_ID_WRITE_SPANS: + return METHOD_NAME_WRITE_SPANS + default: + return "" + } +} + +func HrpcMethodNameToId(name string) uint32 { + switch name { + case METHOD_NAME_WRITE_SPANS: + return METHOD_ID_WRITE_SPANS + default: + return METHOD_ID_NONE + } +} http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/39e89ea0/htrace-htraced/src/go/src/org/apache/htrace/common/span.go ---------------------------------------------------------------------- diff --git a/htrace-htraced/src/go/src/org/apache/htrace/common/span.go b/htrace-htraced/src/go/src/org/apache/htrace/common/span.go new file mode 100644 index 0000000..c273ad9 --- /dev/null +++ b/htrace-htraced/src/go/src/org/apache/htrace/common/span.go @@ -0,0 +1,136 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +package common + +import ( + "encoding/json" + "errors" + "fmt" + "strconv" +) + +// +// Represents a trace span. +// +// Compatibility notes: +// When converting to JSON, we store the 64-bit numbers as hexadecimal strings rather than as +// integers. This is because JavaScript lacks the ability to handle 64-bit integers. Numbers above +// about 55 bits will be rounded by Javascript. Since the Javascript UI is a primary consumer of +// this JSON data, we have to simply pass it as a string. +// + +type TraceInfoMap map[string]string + +type TimelineAnnotation struct { + Time int64 `json:"t"` + Msg string `json:"m"` +} + +type SpanId uint64 + +func (id SpanId) String() string { + return fmt.Sprintf("%016x", uint64(id)) +} + +func (id SpanId) Val() uint64 { + return uint64(id) +} + +func (id SpanId) MarshalJSON() ([]byte, error) { + return []byte(`"` + fmt.Sprintf("%016x", uint64(id)) + `"`), nil +} + +type SpanSlice []*Span + +func (s SpanSlice) Len() int { + return len(s) +} + +func (s SpanSlice) Less(i, j int) bool { + return s[i].Id < s[j].Id +} + +func (s SpanSlice) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} + +type SpanIdSlice []SpanId + +func (s SpanIdSlice) Len() int { + return len(s) +} + +func (s SpanIdSlice) Less(i, j int) bool { + return s[i] < s[j] +} + +func (s SpanIdSlice) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} + +const DOUBLE_QUOTE = 0x22 + +func (id *SpanId) UnmarshalJSON(b []byte) error { + if b[0] != DOUBLE_QUOTE { + return errors.New("Expected spanID to start with a string quote.") + } + if b[len(b)-1] != DOUBLE_QUOTE { + return errors.New("Expected spanID to end with a string quote.") + } + return id.FromString(string(b[1 : len(b)-1])) +} + +func (id *SpanId) FromString(str string) error { + v, err := strconv.ParseUint(str, 16, 64) + if err != nil { + return err + } + *id = SpanId(v) + return nil +} + +type SpanData struct { + Begin int64 `json:"b"` + End int64 `json:"e"` + Description string `json:"d"` + TraceId SpanId `json:"i"` + Parents []SpanId `json:"p"` + Info TraceInfoMap `json:"n,omitempty"` + ProcessId string `json:"r"` + TimelineAnnotations []TimelineAnnotation `json:"t,omitempty"` +} + +type Span struct { + Id SpanId `json:"s"` + SpanData +} + +func (span *Span) ToJson() []byte { + jbytes, err := json.Marshal(*span) + if err != nil { + panic(err) + } + return jbytes +} + +// Compute the span duration. We ignore overflow since we never deal with negative times. +func (span *Span) Duration() int64 { + return span.End - span.Begin +} http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/39e89ea0/htrace-htraced/src/go/src/org/apache/htrace/common/span_test.go ---------------------------------------------------------------------- diff --git a/htrace-htraced/src/go/src/org/apache/htrace/common/span_test.go b/htrace-htraced/src/go/src/org/apache/htrace/common/span_test.go new file mode 100644 index 0000000..f218b3a --- /dev/null +++ b/htrace-htraced/src/go/src/org/apache/htrace/common/span_test.go @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +package common + +import ( + "testing" +) + +func TestSpanToJson(t *testing.T) { + t.Parallel() + span := Span{Id: 2305843009213693952, + SpanData: SpanData{ + Begin: 123, + End: 456, + Description: "getFileDescriptors", + TraceId: 999, + Parents: []SpanId{}, + ProcessId: "testProcessId", + }} + ExpectStrEqual(t, + `{"s":"2000000000000000","b":123,"e":456,"d":"getFileDescriptors","i":"00000000000003e7","p":[],"r":"testProcessId"}`, + string(span.ToJson())) +} + +func TestAnnotatedSpanToJson(t *testing.T) { + t.Parallel() + span := Span{Id: 1305813009213693952, + SpanData: SpanData{ + Begin: 1234, + End: 4567, + Description: "getFileDescriptors2", + TraceId: 999, + Parents: []SpanId{}, + ProcessId: "testAnnotatedProcessId", + TimelineAnnotations: []TimelineAnnotation{ + TimelineAnnotation{ + Time: 7777, + Msg: "contactedServer", + }, + TimelineAnnotation{ + Time: 8888, + Msg: "passedFd", + }, + }, + }} + ExpectStrEqual(t, + `{"s":"121f2e036d442000","b":1234,"e":4567,"d":"getFileDescriptors2","i":"00000000000003e7","p":[],"r":"testAnnotatedProcessId","t":[{"t":7777,"m":"contactedServer"},{"t":8888,"m":"passedFd"}]}`, + string(span.ToJson())) +} http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/39e89ea0/htrace-htraced/src/go/src/org/apache/htrace/common/test_util.go ---------------------------------------------------------------------- diff --git a/htrace-htraced/src/go/src/org/apache/htrace/common/test_util.go b/htrace-htraced/src/go/src/org/apache/htrace/common/test_util.go new file mode 100644 index 0000000..871c847 --- /dev/null +++ b/htrace-htraced/src/go/src/org/apache/htrace/common/test_util.go @@ -0,0 +1,74 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +package common + +import ( + "fmt" + "testing" + "time" +) + +type Int64Slice []int64 + +func (p Int64Slice) Len() int { return len(p) } +func (p Int64Slice) Less(i, j int) bool { return p[i] < p[j] } +func (p Int64Slice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } + +type SupplierFun func() bool + +// +// Wait for a configurable amount of time for a precondition to become true. +// +// Example: +// WaitFor(time.Minute * 1, time.Millisecond * 1, func() bool { +// return ht.Store.GetStatistics().NumSpansWritten >= 3 +// }) +// +func WaitFor(dur time.Duration, poll time.Duration, fun SupplierFun) { + if poll == 0 { + poll = dur / 10 + } + if poll <= 0 { + panic("Can't have a polling time less than zero.") + } + endTime := time.Now().Add(dur) + for { + if fun() { + return + } + if !time.Now().Before(endTime) { + break + } + time.Sleep(poll) + } + panic(fmt.Sprintf("Timed out after %s", dur)) +} + +// Trigger a test failure if two strings are not equal. +func ExpectStrEqual(t *testing.T, expect string, actual string) { + if expect != actual { + t.Fatalf("Expected:\n%s\nGot:\n%s\n", expect, actual) + } +} + +// Trigger a test failure if the JSON representation of two spans are not equals. +func ExpectSpansEqual(t *testing.T, spanA *Span, spanB *Span) { + ExpectStrEqual(t, string(spanA.ToJson()), string(spanB.ToJson())) +} http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/39e89ea0/htrace-htraced/src/go/src/org/apache/htrace/conf/config.go ---------------------------------------------------------------------- diff --git a/htrace-htraced/src/go/src/org/apache/htrace/conf/config.go b/htrace-htraced/src/go/src/org/apache/htrace/conf/config.go new file mode 100644 index 0000000..6093649 --- /dev/null +++ b/htrace-htraced/src/go/src/org/apache/htrace/conf/config.go @@ -0,0 +1,266 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +package conf + +import ( + "bufio" + "bytes" + "fmt" + "io" + "log" + "os" + "path/filepath" + "sort" + "strconv" + "strings" + "syscall" +) + +// +// The configuration code for HTraced. +// +// HTraced can be configured via Hadoop-style XML configuration files, or by passing -Dkey=value +// command line arguments. Command-line arguments without an equals sign, such as "-Dkey", will be +// treated as setting the key to "true". +// +// Configuration key constants should be defined in config_keys.go. Each key should have a default, +// which will be used if the user supplies no value, or supplies an invalid value. +// For that reason, it is not necessary for the Get, GetInt, etc. functions to take a default value +// argument. +// +// Configuration objects are immutable. However, you can make a copy of a configuration which adds +// some changes using Configuration#Clone(). +// + +type Config struct { + settings map[string]string + defaults map[string]string +} + +type Builder struct { + // If non-nil, the XML configuration file to read. + Reader io.Reader + + // If non-nil, the configuration values to use. + Values map[string]string + + // If non-nil, the default configuration values to use. + Defaults map[string]string + + // If non-nil, the command-line arguments to use. + Argv []string +} + +func getHTracedConfDirs(dlog io.Writer) []string { + confDir := os.Getenv("HTRACED_CONF_DIR") + io.WriteString(dlog, fmt.Sprintf("HTRACED_CONF_DIR=%s\n", confDir)) + paths := filepath.SplitList(confDir) + if len(paths) < 1 { + return []string{"."} + } + return paths +} + +// Load a configuration from the application's argv, configuration file, and the standard +// defaults. +func LoadApplicationConfig() (*Config, io.Reader) { + dlog := new(bytes.Buffer) + reader := openFile(CONFIG_FILE_NAME, getHTracedConfDirs(dlog), dlog) + bld := Builder{} + if reader != nil { + defer reader.Close() + bld.Reader = bufio.NewReader(reader) + } + bld.Argv = os.Args[1:] + bld.Defaults = DEFAULTS + cnf, err := bld.Build() + if err != nil { + log.Fatal("Error building configuration: " + err.Error()) + } + os.Args = append(os.Args[0:1], bld.Argv...) + keys := make(sort.StringSlice, 0, 20) + for k, _ := range cnf.settings { + keys = append(keys, k) + } + sort.Sort(keys) + for i := range keys { + io.WriteString(dlog, fmt.Sprintf("%s = %s\n", + keys[i], cnf.settings[keys[i]])) + } + return cnf, dlog +} + +// Attempt to open a configuration file somewhere on the provided list of paths. +func openFile(cnfName string, paths []string, dlog io.Writer) io.ReadCloser { + for p := range paths { + path := fmt.Sprintf("%s%c%s", paths[p], os.PathSeparator, cnfName) + file, err := os.Open(path) + if err == nil { + io.WriteString(dlog, fmt.Sprintf("Reading configuration from %s.\n", path)) + return file + } + if e, ok := err.(*os.PathError); ok && e.Err == syscall.ENOENT { + continue + } + io.WriteString(dlog, fmt.Sprintf("Error opening %s for read: %s\n", path, err.Error())) + } + return nil +} + +// Try to parse a command-line element as a key=value pair. +func parseAsConfigFlag(flag string) (string, string) { + var confPart string + if strings.HasPrefix(flag, "-D") { + confPart = flag[2:] + } else if strings.HasPrefix(flag, "--D") { + confPart = flag[3:] + } else { + return "", "" + } + if len(confPart) == 0 { + return "", "" + } + idx := strings.Index(confPart, "=") + if idx == -1 { + return confPart, "true" + } + return confPart[0:idx], confPart[idx+1:] +} + +// Build a new configuration object from the provided conf.Builder. +func (bld *Builder) Build() (*Config, error) { + // Load values and defaults + cnf := Config{} + cnf.settings = make(map[string]string) + if bld.Values != nil { + for k, v := range bld.Values { + cnf.settings[k] = v + } + } + cnf.defaults = make(map[string]string) + if bld.Defaults != nil { + for k, v := range bld.Defaults { + cnf.defaults[k] = v + } + } + + // Process the configuration file, if we have one + if bld.Reader != nil { + parseXml(bld.Reader, cnf.settings) + } + + // Process command line arguments + var i int + for i < len(bld.Argv) { + str := bld.Argv[i] + key, val := parseAsConfigFlag(str) + if key != "" { + if val == "" { + cnf.settings[key] = "true" + } else { + cnf.settings[key] = val + } + bld.Argv = append(bld.Argv[:i], bld.Argv[i+1:]...) + } else { + i++ + } + } + return &cnf, nil +} + +// Returns true if the configuration has a non-default value for the given key. +func (cnf *Config) Contains(key string) bool { + _, ok := cnf.settings[key] + return ok +} + +// Get a string configuration key. +func (cnf *Config) Get(key string) string { + ret := cnf.settings[key] + if ret != "" { + return ret + } + return cnf.defaults[key] +} + +// Get a boolean configuration key. +func (cnf *Config) GetBool(key string) bool { + str := cnf.settings[key] + ret, err := strconv.ParseBool(str) + if err == nil { + return ret + } + str = cnf.defaults[key] + ret, err = strconv.ParseBool(str) + if err == nil { + return ret + } + return false +} + +// Get an integer configuration key. +func (cnf *Config) GetInt(key string) int { + str := cnf.settings[key] + ret, err := strconv.Atoi(str) + if err == nil { + return ret + } + str = cnf.defaults[key] + ret, err = strconv.Atoi(str) + if err == nil { + return ret + } + return 0 +} + +// Get an int64 configuration key. +func (cnf *Config) GetInt64(key string) int64 { + str := cnf.settings[key] + ret, err := strconv.ParseInt(str, 10, 64) + if err == nil { + return ret + } + str = cnf.defaults[key] + ret, err = strconv.ParseInt(str, 10, 64) + if err == nil { + return ret + } + return 0 +} + +// Make a deep copy of the given configuration. +// Optionally, you can specify particular key/value pairs to change. +// Example: +// cnf2 := cnf.Copy("my.changed.key", "my.new.value") +func (cnf *Config) Clone(args ...string) *Config { + if len(args)%2 != 0 { + panic("The arguments to Config#copy are key1, value1, " + + "key2, value2, and so on. You must specify an even number of arguments.") + } + ncnf := &Config{defaults: cnf.defaults} + ncnf.settings = make(map[string]string) + for k, v := range cnf.settings { + ncnf.settings[k] = v + } + for i := 0; i < len(args); i += 2 { + ncnf.settings[args[i]] = args[i+1] + } + return ncnf +}
