TS-4099: add generic Lua bindings for metrics

This adds binding for metrics such that metric value can be queried
from within Lua by using their full names.


Project: http://git-wip-us.apache.org/repos/asf/trafficserver/repo
Commit: http://git-wip-us.apache.org/repos/asf/trafficserver/commit/e9649d2c
Tree: http://git-wip-us.apache.org/repos/asf/trafficserver/tree/e9649d2c
Diff: http://git-wip-us.apache.org/repos/asf/trafficserver/diff/e9649d2c

Branch: refs/heads/master
Commit: e9649d2c2e31c0b65f42e3db9c5e354c630b53a9
Parents: 5942c42
Author: James Peach <[email protected]>
Authored: Tue Nov 24 16:08:25 2015 -0800
Committer: James Peach <[email protected]>
Committed: Thu Jan 21 18:52:18 2016 -0800

----------------------------------------------------------------------
 lib/bindings/Makefile.am |   2 +
 lib/bindings/metrics.cc  | 201 ++++++++++++++++++++++++++++++++++++++++++
 lib/bindings/metrics.h   |  33 +++++++
 3 files changed, 236 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/trafficserver/blob/e9649d2c/lib/bindings/Makefile.am
----------------------------------------------------------------------
diff --git a/lib/bindings/Makefile.am b/lib/bindings/Makefile.am
index 661411c..3ea6f63 100644
--- a/lib/bindings/Makefile.am
+++ b/lib/bindings/Makefile.am
@@ -33,6 +33,8 @@ libbindings_la_SOURCES = \
   bindings.h \
   lua.cc \
   lua.h \
+  metrics.cc \
+  metrics.h \
   repl.cc \
   repl.h
 

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/e9649d2c/lib/bindings/metrics.cc
----------------------------------------------------------------------
diff --git a/lib/bindings/metrics.cc b/lib/bindings/metrics.cc
new file mode 100644
index 0000000..66dbacf
--- /dev/null
+++ b/lib/bindings/metrics.cc
@@ -0,0 +1,201 @@
+/** @file
+
+  Lua bindings for librecords.
+
+  @section license License
+
+  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.
+ */
+
+#include "bindings.h"
+#include "metrics.h"
+#include "P_RecCore.h"
+#include "ts/ink_memory.h"
+#include <map>
+
+#define BINDING "lua.metrics"
+
+struct metrics_binding {
+  static metrics_binding *check(lua_State *L, int index);
+
+  typedef std::map<std::string, int> ref_map;
+
+  ats_scoped_str prefix;
+  size_t prefixlen;
+  ref_map refs;
+};
+
+metrics_binding *
+metrics_binding::check(lua_State *L, int index)
+{
+  metrics_binding *m;
+
+  luaL_checktype(L, index, LUA_TUSERDATA);
+  m = (metrics_binding *)luaL_checkudata(L, index, BINDING);
+  if (m == NULL) {
+    luaL_typerror(L, index, "userdata");
+  }
+
+  return m;
+}
+
+static bool
+metrics_record_exists(const char *name)
+{
+  RecT rec_type;
+  return RecGetRecordType(name, &rec_type) == REC_ERR_OKAY;
+}
+
+// Push the value of a record onto the Lua stack.
+static void
+metrics_push_record(const RecRecord *rec, void *ptr)
+{
+  lua_State *L = (lua_State *)ptr;
+
+  ink_assert(REC_TYPE_IS_STAT(rec->rec_type));
+
+  switch (rec->data_type) {
+  case RECD_INT: /* fallthru */
+  case RECD_COUNTER:
+    lua_pushinteger(L, rec->data.rec_int);
+    break;
+  case RECD_FLOAT:
+    lua_pushnumber(L, rec->data.rec_float);
+    break;
+  case RECD_STRING:
+    lua_pushlstring(L, rec->data.rec_string, strlen(rec->data.rec_string));
+    break;
+  default:
+    lua_pushnil(L);
+  }
+}
+
+// Return the value of a metric, relative to the bound prefix.
+static int
+metrics_index(lua_State *L)
+{
+  metrics_binding *m = metrics_binding::check(L, 1);
+  metrics_binding::ref_map::iterator ptr;
+
+  const char *key;
+  size_t len;
+
+  key = luaL_checklstring(L, 2, &len);
+  ink_release_assert(key != NULL && len != 0);
+
+  // First, check whether we have a reference stored for this key.
+  ptr = m->refs.find(std::string(key, len));
+  if (ptr != m->refs.end()) {
+    // We have a ref, so push the saved table reference to the stack.
+    lua_rawgeti(L, LUA_REGISTRYINDEX, ptr->second);
+  } else {
+    char name[m->prefixlen + sizeof(".") + len];
+
+    snprintf(name, sizeof(name), "%s.%.*s", m->prefix.get(), (int)len, key);
+
+    // Push the indexed record value, or nil if there is nothing there.
+    if (RecLookupRecord(name, metrics_push_record, L) != REC_ERR_OKAY) {
+      lua_pushnil(L);
+    }
+  }
+
+  return 1;
+}
+
+static int
+metrics_newindex(lua_State *L)
+{
+  // The stack now looks like:
+  //  1   the table value (userdata)
+  //  2   key to index (string)
+  //  3   value to insert (should be a table)
+
+  metrics_binding *m = metrics_binding::check(L, 1);
+  const char *key;
+  size_t len;
+  metrics_binding::ref_map::iterator ptr;
+
+  key = luaL_checklstring(L, 2, &len);
+  switch (lua_type(L, 3)) {
+  case LUA_TUSERDATA:
+    metrics_binding::check(L, 3);
+    break;
+  case LUA_TTABLE:
+    break;
+  default:
+    luaL_typerror(L, 3, "userdata or table");
+  }
+
+  char name[m->prefixlen + sizeof(".") + len];
+
+  snprintf(name, sizeof(name), "%s.%.*s", m->prefix.get(), (int)len, key);
+
+  // If this index is already a record, don't overwrite it.
+  if (metrics_record_exists(name)) {
+    return 0;
+  }
+
+  ptr = m->refs.find(std::string(key, len));
+  if (ptr != m->refs.end()) {
+    // Remove the previously saved reference.
+    luaL_unref(L, LUA_REGISTRYINDEX, ptr->second);
+  }
+
+  // Pop the top of the stack into a reference that we store in the refmap.
+  lua_pushvalue(L, 3);
+  m->refs[std::string(key, len)] = luaL_ref(L, LUA_REGISTRYINDEX);
+
+  return 0;
+}
+
+static int
+metrics_gc(lua_State *L)
+{
+  metrics_binding *m = metrics_binding::check(L, 1);
+
+  // Clean up any references we stashed.
+  for (metrics_binding::ref_map::iterator ptr = m->refs.begin(); ptr != 
m->refs.end(); ++ptr) {
+    luaL_unref(L, LUA_REGISTRYINDEX, ptr->second);
+  }
+
+  m->~metrics_binding();
+  return 0;
+}
+
+int
+lua_metrics_new(const char *prefix, lua_State *L)
+{
+  metrics_binding *m = lua_newuserobject<metrics_binding>(L);
+
+  Debug("lua", "new metrics binding for prefix %s", prefix);
+  m->prefix = ats_strdup(prefix);
+  m->prefixlen = strlen(prefix);
+
+  luaL_getmetatable(L, BINDING);
+  lua_setmetatable(L, -2);
+
+  // Leave the userdata on the stack.
+  return 1;
+}
+
+void
+lua_metrics_register(lua_State *L)
+{
+  static const luaL_reg metatable[] = {{"__gc", metrics_gc}, {"__index", 
metrics_index}, {"__newindex", metrics_newindex}, {0, 0}};
+
+  BindingInstance::register_metatable(L, BINDING, metatable);
+}

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/e9649d2c/lib/bindings/metrics.h
----------------------------------------------------------------------
diff --git a/lib/bindings/metrics.h b/lib/bindings/metrics.h
new file mode 100644
index 0000000..e0d39ac
--- /dev/null
+++ b/lib/bindings/metrics.h
@@ -0,0 +1,33 @@
+/** @file
+ *
+ *  A brief file description
+ *
+ *  @section license License
+ *
+ *  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.
+ */
+
+#ifndef METRICS_H_FED1F5EA_9EDE_48E6_B05A_5DCAFD8DC319
+#define METRICS_H_FED1F5EA_9EDE_48E6_B05A_5DCAFD8DC319
+
+// Create a new metrics binding userdata object.
+int lua_metrics_new(const char *prefix, lua_State *L);
+
+// Register metrics binding type metatable.
+void lua_metrics_register(lua_State *L);
+
+#endif /* METRICS_H_FED1F5EA_9EDE_48E6_B05A_5DCAFD8DC319 */

Reply via email to