diff --git a/WORKSPACE b/WORKSPACE index fbbc290..7bfd85c 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -501,3 +501,19 @@ new_git_repository( commit = "042f812382aa16eada6078594582150fa5dc7235", remote = "https://github.com/eberlitz/material-steppers.git", ) + +# FBN +new_http_archive( + name = "datadog_archive", + build_file = "//third_party:datadog.BUILD", + sha256 = "86cef95acd73543d18c417f1b0313c0a7274ed8f5ae9cceb46314f4e588085b1", + strip_prefix = "datadog-0.22.0", + urls = [ + "https://files.pythonhosted.org/packages/29/45/4f21ad21de22c7abe64f340e6fe1ebc412bb1e8bb580dd963fd70ac86441/datadog-0.22.0.tar.gz", + ], +) + +bind( + name = "datadog", + actual = "@datadog_archive//:datadog", +) \ No newline at end of file diff --git a/third_party/datadog.BUILD b/third_party/datadog.BUILD new file mode 100644 index 0000000..b4cfdb2 --- /dev/null +++ b/third_party/datadog.BUILD @@ -0,0 +1,10 @@ +licenses(["notice"]) # datadog + +py_library( + name = "datadog", + srcs = glob(["**/*.py"]), + srcs_version = "PY2AND3", + data = glob(["*.egg-info/**"]), + visibility = ["//visibility:public"], + deps = [], +) \ No newline at end of file diff --git a/upvote/gae/modules/upvote_app/frontend/admin_ui/settingspage/settingspage-controller.js b/upvote/gae/modules/upvote_app/frontend/admin_ui/settingspage/settingspage-controller.js index 364bc8f..2ba7dd4 100644 --- a/upvote/gae/modules/upvote_app/frontend/admin_ui/settingspage/settingspage-controller.js +++ b/upvote/gae/modules/upvote_app/frontend/admin_ui/settingspage/settingspage-controller.js @@ -19,7 +19,7 @@ goog.require('upvote.admin.settings.SettingsService'); goog.require('upvote.shared.Page'); -/** @typedef {{virustotal: string, bit9:string}} */ +/** @typedef {{virustotal: string, bit9:string, datadog:string}} */ upvote.admin.settingspage.ApiKeyStruct; /** @@ -77,6 +77,7 @@ upvote.admin.settingspage.SettingsController = class { this.apiKeys = { 'virustotal': '', 'bit9': '', + 'datadog': '', }; page.title = 'Settings'; diff --git a/upvote/gae/shared/common/BUILD b/upvote/gae/shared/common/BUILD index 6440bd2..9bfa668 100644 --- a/upvote/gae/shared/common/BUILD +++ b/upvote/gae/shared/common/BUILD @@ -81,7 +81,11 @@ py_appengine_library( py_appengine_library( name = "monitoring", srcs = ["monitoring.py"], - deps = ["//upvote/shared:constants"], + deps = [ + "//upvote/shared:constants", + "//upvote/gae/datastore/models:datadog", + "//external:datadog" + ], ) py_appengine_library( diff --git a/upvote/gae/shared/common/monitoring.py b/upvote/gae/shared/common/monitoring.py index 530c011..7119fa1 100644 --- a/upvote/gae/shared/common/monitoring.py +++ b/upvote/gae/shared/common/monitoring.py @@ -17,6 +17,40 @@ import functools import logging +from upvote.gae.datastore.models import datadog as datadog_model + +import datadog + +# datadog metric name rules: https://help.datadoghq.com/hc/en-us/articles/203764705-What-are-valid-metric-names- + +_dd_stats = None + + +def _dd_get_stats(): + global _dd_stats + + if not _dd_stats: + dd_api_key = datadog_model.DataDogApiAuth.GetInstance() + if not dd_api_key: + return None + + datadog.initialize(dd_api_key) + + _dd_stats = datadog.ThreadStats() + _dd_stats.start() + + return _dd_stats + + +def _dd_get_format(metric, fields): + stat_format = metric.metric_name + if not fields: + return stat_format + + for (field_name, field_type) in fields: + stat_format += u"." + field_name + u"%s" + return stat_format + def ContainExceptions(func): @@ -38,11 +72,13 @@ def __init__(self, metric, value_type, fields=None): self.metric_name = metric.metric_name self.type_ = value_type self.fields = fields + self._stat_format = _dd_get_format(metric, fields) @ContainExceptions def Set(self, value, *args): - # Implement setting a metric - pass + stats = _dd_get_stats() + if stats: + stats.gauge(self._stat_format % args, value) class LatencyMetric(object): @@ -52,11 +88,13 @@ def __init__(self, metric, fields=None): self.display_name = metric.display_name self.metric_name = metric.metric_name self.fields = fields + self._stat_format = _dd_get_format(metric, fields) @ContainExceptions def Record(self, value, *args): - # Implement recording a latency value - pass + stats = _dd_get_stats() + if stats: + stats.gauge(self._stat_format % args, value) class Counter(object): @@ -66,16 +104,17 @@ def __init__(self, metric, fields=None): self.display_name = metric.display_name self.metric_name = metric.metric_name self.fields = fields + self._stat_format = _dd_get_format(metric, fields) @ContainExceptions def Increment(self, *args): - # Implement incrementing a metric - pass + stats = _dd_get_stats() + if stats: + stats.increment(self._stat_format % args) @ContainExceptions def IncrementBy(self, inc, *args): - # Implement incrementing a metric by N - pass + _dd_get_stats().increment(self._stat_format % args, inc) class RequestCounter(Counter):