Module: Hastur
Overview
Hastur API gem that allows services/apps to easily publish correct Hastur-commands to their local machine’s UDP sockets. Bare minimum for all JSON packets is to have :type key/values to map to a hastur message type, which the router uses for sink delivery.
Constant Summary collapse
- SECS_2100 =
4102444800
- MILLI_SECS_2100 =
4102444800000
- MICRO_SECS_2100 =
4102444800000000
- NANO_SECS_2100 =
4102444800000000000
- SECS_1971 =
31536000
- MILLI_SECS_1971 =
31536000000
- MICRO_SECS_1971 =
31536000000000
- NANO_SECS_1971 =
31536000000000000
- PLUGIN_INTERVALS =
[ :five_minutes, :thirty_minutes, :hourly, :daily, :monthly ]
- START_OPTS =
[ :background_thread ]
- VERSION =
"1.2.8"
Class Attribute Summary collapse
-
.mutex ⇒ Object
Returns the value of attribute mutex.
Instance Method Summary collapse
-
#add_default_labels(new_default_labels) ⇒ Object
Add default labels which will be sent back with every Hastur message sent by this process.
-
#app_name ⇒ String
(also: #application)
Attempts to determine the application name, or uses an application-provided one, if set.
-
#app_name=(new_name) ⇒ Object
(also: #application=)
Set the application name that Hastur registers as.
-
#background_thread? ⇒ Boolean
Returns whether the background thread is currently running.
-
#counter(name, value = 1, timestamp = :now, labels = {}) ⇒ Object
Sends a ‘counter’ stat to Hastur.
-
#deliver_with(&block) ⇒ Object
Set delivery method to the given proc/block.
-
#epoch_usec(timestamp = Time.now) ⇒ Fixnum
(also: #timestamp)
Best effort to make all timestamps be Hastur timestamps, 64 bit numbers that represent the total number of microseconds since Jan 1, 1970 at midnight UTC.
-
#event(name, subject = nil, body = nil, attn = [], timestamp = :now, labels = {}) ⇒ Object
Sends an event to Hastur.
-
#every(interval) { ... } ⇒ Object
Runs a block of code periodically every interval.
-
#gauge(name, value, timestamp = :now, labels = {}) ⇒ Object
Sends a ‘gauge’ stat to Hastur.
-
#heartbeat(name = "application.heartbeat", value = nil, timeout = nil, timestamp = :now, labels = {}) ⇒ Object
Sends a heartbeat to Hastur.
-
#info_agent(tag, data = {}, timestamp = :now, labels = {}) ⇒ Object
This sends back freeform data about the agent or host that Hastur is running on.
-
#info_process(tag, data = {}, timestamp = :now, labels = {}) ⇒ Object
Sends freeform process information to Hastur.
-
#kill_background_thread ⇒ Object
This should ordinarily only be for testing.
-
#log(subject = nil, data = {}, timestamp = :now, labels = {}) ⇒ Object
Sends a log line to Hastur.
-
#mark(name, value = nil, timestamp = :now, labels = {}) ⇒ Object
Sends a ‘mark’ stat to Hastur.
- #message_name_prefix ⇒ Object
-
#message_name_prefix=(value) ⇒ Object
Set a message-name prefix for all message types that have names.
-
#no_background_thread! ⇒ Object
Prevents starting a background thread under any circumstances.
-
#register_plugin(name, plugin_path, plugin_args, plugin_interval, timestamp = :now, labels = {}) ⇒ Object
Sends a plugin registration to Hastur.
-
#register_process(name = app_name, data = {}, timestamp = :now, labels = {}) ⇒ Object
Sends a process registration to Hastur.
-
#remove_default_label_names(*default_label_keys) ⇒ Object
Remove default labels which will be sent back with every Hastur message sent by this process.
-
#reset ⇒ Object
Reset Hastur module for tests.
-
#reset_default_labels ⇒ Object
Reset the default labels which will be sent back with every Hastur message sent by this process.
-
#start(opts = {}) ⇒ Object
Start Hastur’s background thread and/or do process registration or neither, according to what options are set.
-
#start_background_thread ⇒ Object
Starts a background thread that will execute blocks of code every so often.
-
#time(name, timestamp = nil, labels = {}) ⇒ Object
Run the block and report its runtime back to Hastur as a gauge.
-
#udp_port=(new_port) ⇒ Object
Set the UDP port.
Class Attribute Details
.mutex ⇒ Object
Returns the value of attribute mutex.
25 26 27 |
# File 'lib/hastur/api.rb', line 25 def mutex @mutex end |
Instance Method Details
#add_default_labels(new_default_labels) ⇒ Object
Add default labels which will be sent back with every Hastur message sent by this process. The labels will be sent back with the same constant value each time that is specified in the labels hash.
This is a useful way to send back information that won’t change during the run, or that will change only occasionally like resource usage, server information, deploy environment, etc. The same kind of information can be sent back using info_process(), so consider which way makes more sense for your case.
201 202 203 204 205 |
# File 'lib/hastur/api.rb', line 201 def add_default_labels(new_default_labels) @default_labels ||= {} @default_labels.merge! end |
#app_name ⇒ String Also known as: application
Attempts to determine the application name, or uses an application-provided one, if set. In order, Hastur checks:
-
User-provided app name via Hastur.app_name=
-
HASTUR_APP_NAME environment variable
-
::HASTUR_APP_NAME Ruby constant
-
Ecology.application, if set
-
File.basename($0)
153 154 155 156 157 158 159 160 161 162 163 164 165 |
# File 'lib/hastur/api.rb', line 153 def app_name return @app_name if @app_name return @app_name = ENV['HASTUR_APP_NAME'] if ENV['HASTUR_APP_NAME'] top_level = ::HASTUR_APP_NAME rescue nil return @app_name = top_level if top_level eco = Ecology rescue nil return @app_name = Ecology.application if eco @app_name = File.basename $0 end |
#app_name=(new_name) ⇒ Object Also known as: application=
Set the application name that Hastur registers as.
173 174 175 176 177 178 179 180 181 182 183 184 |
# File 'lib/hastur/api.rb', line 173 def app_name=(new_name) old_name = @app_name @app_name = new_name if @process_registration_done err_str = "You changed the application name from #{old_name} to " + "#{new_name} after the process was registered!" STDERR.puts err_str Hastur.log err_str end end |
#background_thread? ⇒ Boolean
Debug this.
Returns whether the background thread is currently running.
102 103 104 |
# File 'lib/hastur/api.rb', line 102 def background_thread? @bg_thread && !@bg_thread.alive? end |
#counter(name, value = 1, timestamp = :now, labels = {}) ⇒ Object
Sends a ‘counter’ stat to Hastur. Counters are linear, and are sent as deltas (differences). Sending a value of 1 adds 1 to the counter.
494 495 496 497 498 499 500 |
# File 'lib/hastur/api.rb', line 494 def counter(name, value=1, =:now, labels={}) send_to_udp :type => :counter, :name => + (name || ""), :value => value, :timestamp => epoch_usec(), :labels => default_labels.merge(labels) end |
#deliver_with(&block) ⇒ Object
Set delivery method to the given proc/block. The block is saved and called with each message to be sent. If no block is given or if this method is not called, the delivery method defaults to sending over the configured UDP port.
447 448 449 |
# File 'lib/hastur/api.rb', line 447 def deliver_with(&block) @__delivery_method__ = block end |
#epoch_usec(timestamp = Time.now) ⇒ Fixnum Also known as: timestamp
Best effort to make all timestamps be Hastur timestamps, 64 bit numbers that represent the total number of microseconds since Jan 1, 1970 at midnight UTC. Accepts second, millisecond or nanosecond timestamps and Ruby times. You can also give :now or nil for Time.now.
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
# File 'lib/hastur/api.rb', line 116 def epoch_usec(=Time.now) = Time.now if .nil? || == :now case when Time (.to_f*1000000).to_i when DateTime # Ruby 1.8.7 doesn't have to DateTime#to_time or DateTime#to_f method. # For right now, declare failure. raise "Ruby DateTime objects are not yet supported!" when SECS_1971..SECS_2100 * 1000000 when MILLI_SECS_1971..MILLI_SECS_2100 * 1000 when MICRO_SECS_1971..MICRO_SECS_2100 when NANO_SECS_1971..NANO_SECS_2100 / 1000 else raise "Unable to validate timestamp: #{}" end end |
#event(name, subject = nil, body = nil, attn = [], timestamp = :now, labels = {}) ⇒ Object
Sends an event to Hastur. An event is high-priority and never buffered, and will be sent preferentially to stats or heartbeats. It includes an end-to-end acknowledgement to ensure arrival, but is expensive to store, send and query.
‘Attn’ is a mechanism to describe the system or component in which the event occurs and who would care about it. Obvious values to include in the array include user logins, email addresses, team names, and server, library or component names. This allows making searches like “what events should I worry about?” or “what events have recently occurred on the Rails server?”
539 540 541 542 543 544 545 546 547 |
# File 'lib/hastur/api.rb', line 539 def event(name, subject=nil, body=nil, attn=[], =:now, labels={}) send_to_udp :type => :event, :name => + (name || ""), :subject => subject.to_s[0...3_072], :body => body.to_s[0...3_072], :attn => [ attn ].flatten, :timestamp => epoch_usec(), :labels => default_labels.merge(labels) end |
#every(interval) { ... } ⇒ Object
Runs a block of code periodically every interval. Use this method to report statistics at a fixed time interval.
718 719 720 721 722 723 724 725 726 727 728 729 730 731 |
# File 'lib/hastur/api.rb', line 718 def every(interval, &block) if @prevent_background_thread log("You called .every(), but background threads are specifically prevented.") end unless @intervals.include?(interval) raise "Interval must be one of these: #{@intervals}, you gave #{interval.inspect}" end # Don't add to existing array. += will create a new array. Then # when we save a reference to the old array and iterate through # it, it won't change midway. Hastur.mutex.synchronize { @scheduled_blocks[interval] += [ block ] } end |
#gauge(name, value, timestamp = :now, labels = {}) ⇒ Object
Sends a ‘gauge’ stat to Hastur. A gauge’s value may or may not be on a linear scale. It is sent as an exact value, not a difference.
512 513 514 515 516 517 518 |
# File 'lib/hastur/api.rb', line 512 def gauge(name, value, =:now, labels={}) send_to_udp :type => :gauge, :name => + (name || ""), :value => value, :timestamp => epoch_usec(), :labels => default_labels.merge(labels) end |
#heartbeat(name = "application.heartbeat", value = nil, timeout = nil, timestamp = :now, labels = {}) ⇒ Object
Sends a heartbeat to Hastur. A heartbeat is a periodic message which indicates that a host, application or service is currently running. It is higher priority than a statistic and should not be batched, but is lower priority than an event does not include an end-to-end acknowledgement.
Plugin results are sent as a heartbeat with the plugin’s name as the heartbeat name.
687 688 689 690 691 692 693 |
# File 'lib/hastur/api.rb', line 687 def heartbeat(name="application.heartbeat", value=nil, timeout = nil, =:now, labels={}) send_to_udp :name => + (name || ""), :type => :hb_process, :value => value, :timestamp => epoch_usec(), :labels => default_labels.merge(labels) end |
#info_agent(tag, data = {}, timestamp = :now, labels = {}) ⇒ Object
This sends back freeform data about the agent or host that Hastur is running on. Sample uses include what libraries or packages are installed and available, the total installed memory
Any number of these can be sent as information changes or is superceded. However, if information changes constantly or needs to be graphed or alerted on, send that separately as a metric or event. Info_agent messages are freeform and not readily separable or graphable.
635 636 637 638 639 640 641 |
# File 'lib/hastur/api.rb', line 635 def info_agent(tag, data = {}, = :now, labels = {}) send_to_udp :type => :info_agent, :tag => tag, :data => data, :timestamp => epoch_usec(), :labels => default_labels.merge(labels) end |
#info_process(tag, data = {}, timestamp = :now, labels = {}) ⇒ Object
Sends freeform process information to Hastur. This can be supplemental information about resources like memory, loaded gems, Ruby version, files open and whatnot. It can be additional configuration or deployment information like environment (dev/staging/prod), software or component version, etc. It can be information about the application as deployed, as run, or as it is currently running.
The default labels contain application name and process ID to match this information with the process registration and similar details.
Any number of these can be sent as information changes or is superceded. However, if information changes constantly or needs to be graphed or alerted on, send that separately as a metric or event. Info_process messages are freeform and not readily separable or graphable.
611 612 613 614 615 616 617 |
# File 'lib/hastur/api.rb', line 611 def info_process(tag, data = {}, = :now, labels = {}) send_to_udp :type => :info_process, :tag => tag, :data => data, :timestamp => epoch_usec(), :labels => default_labels.merge(labels) end |
#kill_background_thread ⇒ Object
This should ordinarily only be for testing. It kills the background thread so that automatic heartbeats and .every() blocks don’t happen. If you restart the background thread, all your .every() blocks go away, but the process heartbeat is restarted.
94 95 96 |
# File 'lib/hastur/api.rb', line 94 def kill_background_thread __kill_bg_thread__ end |
#log(subject = nil, data = {}, timestamp = :now, labels = {}) ⇒ Object
Sends a log line to Hastur. A log line is of relatively low priority, comparable to stats, and is allowed to be buffered or batched while higher-priority data is sent first.
Severity can be included in the data field with the tag “severity” if desired.
562 563 564 565 566 567 568 |
# File 'lib/hastur/api.rb', line 562 def log(subject=nil, data={}, =:now, labels={}) send_to_udp :type => :log, :subject => subject.to_s[0...7_168], :data => data, :timestamp => epoch_usec(), :labels => default_labels.merge(labels) end |
#mark(name, value = nil, timestamp = :now, labels = {}) ⇒ Object
Sends a ‘mark’ stat to Hastur. A mark gives the time that an interesting event occurred even with no value attached. You can also use a mark to send back string-valued stats that might otherwise be guages – “Green”, “Yellow”, “Red” or similar.
It is different from a Hastur event because it happens at stat priority – it can be batched or slightly delayed, and doesn’t have an end-to-end acknowledgement included.
476 477 478 479 480 481 482 |
# File 'lib/hastur/api.rb', line 476 def mark(name, value = nil, =:now, labels={}) send_to_udp :type => :mark, :name => + (name || ""), :value => value, :timestamp => epoch_usec(), :labels => default_labels.merge(labels) end |
#message_name_prefix ⇒ Object
264 265 266 |
# File 'lib/hastur/api.rb', line 264 def @message_name_prefix || "" end |
#message_name_prefix=(value) ⇒ Object
Set a message-name prefix for all message types that have names. It will be prepended automatically for those message types’ names. A nil value will be treated as the empty string. Plugin names don’t count as message names for these purposes, and will not be prefixed.
260 261 262 |
# File 'lib/hastur/api.rb', line 260 def (value) @message_name_prefix = value end |
#no_background_thread! ⇒ Object
Prevents starting a background thread under any circumstances.
45 46 47 |
# File 'lib/hastur/api.rb', line 45 def no_background_thread! @prevent_background_thread = true end |
#register_plugin(name, plugin_path, plugin_args, plugin_interval, timestamp = :now, labels = {}) ⇒ Object
Sends a plugin registration to Hastur. A plugin is a program on the host machine which can be run to determine status of the machine, an application or anything else interesting.
This registration tells Hastur to begin scheduling runs of the plugin and report back on the resulting status codes or crashes.
657 658 659 660 661 662 663 664 665 666 667 668 |
# File 'lib/hastur/api.rb', line 657 def register_plugin(name, plugin_path, plugin_args, plugin_interval, =:now, labels={}) unless PLUGIN_INTERVALS.include?(plugin_interval) raise "Interval must be one of: #{PLUGIN_INTERVALS.join(', ')}" end send_to_udp :type => :reg_pluginv1, :plugin_path => plugin_path, :plugin_args => plugin_args, :interval => plugin_interval, :plugin => name, :timestamp => epoch_usec(), :labels => default_labels.merge(labels) end |
#register_process(name = app_name, data = {}, timestamp = :now, labels = {}) ⇒ Object
Sends a process registration to Hastur. This indicates that the process is currently running, and that heartbeats should be sent for some time afterward.
580 581 582 583 584 585 |
# File 'lib/hastur/api.rb', line 580 def register_process(name = app_name, data = {}, = :now, labels = {}) send_to_udp :type => :reg_process, :data => { "language" => "ruby", "version" => Hastur::VERSION }.merge(data), :timestamp => epoch_usec(), :labels => default_labels.merge(labels) end |
#remove_default_label_names(*default_label_keys) ⇒ Object
Remove default labels which will be sent back with every Hastur message sent by this process. This cannot remove the three automatic defaults (application, pid, tid). Keys that have not been added cannot be removed, and so will be silently ignored (no exception will be raised).
216 217 218 219 220 |
# File 'lib/hastur/api.rb', line 216 def remove_default_label_names(*default_label_keys) keys_to_remove = default_label_keys.flatten keys_to_remove.each { |key| @default_labels.delete(key) } end |
#reset ⇒ Object
Reset Hastur module for tests. This removes all settings and kills the background thread, resetting Hastur to its initial pre-start condition.
238 239 240 241 242 243 244 245 246 247 248 249 250 251 |
# File 'lib/hastur/api.rb', line 238 def reset __kill_bg_thread__ @app_name = nil @prevent_background_thread = nil @process_registration_done = nil @udp_port = nil @__delivery_method__ = nil @scheduled_blocks = nil @last_time = nil @intervals = nil @interval_values = nil @default_labels = nil @message_name_prefix = nil end |
#reset_default_labels ⇒ Object
Reset the default labels which will be sent back with every Hastur message sent by this process. After this, only the automatic default labels (process ID, thread ID, application name) will be sent, plus of course the ones specified for the specific Hastur message call.
229 230 231 |
# File 'lib/hastur/api.rb', line 229 def reset_default_labels @default_labels = {} end |
#start(opts = {}) ⇒ Object
Start Hastur’s background thread and/or do process registration or neither, according to what options are set.
60 61 62 63 64 65 66 67 68 69 70 71 |
# File 'lib/hastur/api.rb', line 60 def start(opts = {}) bad_keys = opts.keys - START_OPTS raise "Unknown options to Hastur.start: #{bad_keys.join(", ")}!" unless bad_keys.empty? unless @prevent_background_thread || (opts.has_key?(:background_thread) && !opts[:background_thread]) start_background_thread end @process_registration_done = true register_process Hastur.app_name, {} end |
#start_background_thread ⇒ Object
Starts a background thread that will execute blocks of code every so often.
76 77 78 79 80 81 82 83 84 85 86 |
# File 'lib/hastur/api.rb', line 76 def start_background_thread if @prevent_background_thread raise "You can't start a background thread! Somebody called .no_background_thread! already." end return if @bg_thread @intervals = [:five_secs, :minute, :hour, :day] @interval_values = [5, 60, 60*60, 60*60*24 ] __reset_bg_thread__ end |
#time(name, timestamp = nil, labels = {}) ⇒ Object
Run the block and report its runtime back to Hastur as a gauge.
703 704 705 706 707 708 709 |
# File 'lib/hastur/api.rb', line 703 def time(name, =nil, labels={}) started = Time.now ret = yield ended = Time.now gauge name, ended - started, || started, labels ret end |
#udp_port=(new_port) ⇒ Object
Set the UDP port. Defaults to 8125
456 457 458 |
# File 'lib/hastur/api.rb', line 456 def udp_port=(new_port) @udp_port = new_port end |