Java Observability Toolkit (JOT) 是一个平台,无需编写任何代码甚至无需重新编译即可观察任何 Java 应用程序。该平台由...
您无需任何编码即可快速轻松地创建 JOT。 JOT 可以使正在运行的 Java 应用程序中的任何内容变得可观察。以下是您可以使用它的一些示例...但唯一的限制是您的创造力。
您在解决生产问题时陷入困境,并且没有足够的日志或调试能力。通过简单的 JOT 规则,您可以快速轻松地获取所需的数据。
您想要监控只能在应用程序内部深处访问的内容,但您没有办法将其公开给您的工具。
您希望防止攻击者滥用强大的方法,但它们位于您无法控制的库中。
JOT 的目标是在不影响简单性的情况下提供最大的仪器功率。
让我们知道您使用 JOT 所做的很酷的事情!并考虑将它们贡献给项目,以便其他人可以分享您的精彩。最简单的贡献方法是创建拉取请求。如果您要共享 JOT,请将其添加到项目的“contrib”目录中...并请添加一条注释,详细说明其用途。
在 yaml 中定义传感器和报告并将其保存在 .jot 文件中。传感器定义要捕获的数据,“报告”告诉 JOT 如何随时间跟踪数据。
sensors:
- name: "get-ciphers"
description: "Identifies encryption ciphers"
methods:
- "javax.crypto.Cipher.getInstance"
captures:
- "#P0"
reports:
- name: "Encryption Usage"
type: "list"
cols: "get-ciphers"
基本上您所要做的就是将 JOT 添加到 JVM
然后您只需正常使用您的应用程序并让 JOT 为您收集数据即可。 JOT 将制作一个很好的表格,准确记录使用加密的位置以及指定的算法。
Encryption Algorithms get-ciphers
------------------------------------------------------------ ----------------------
com.acme.ticketbook.Ticket.encrypt(Ticket.java:125) DES
org.apache.jsp.accessA_jsp._jspService(accessA_jsp.java:212) AES
org.apache.jsp.accessA_jsp._jspService(accessA_jsp.java:213) PBEWithMD5AndTripleDES
org.apache.jsp.accessB_jsp._jspService(accessB_jsp.java:212) DES
org.apache.jsp.accessC_jsp._jspService(accessC_jsp.java:212) DES/CBC/PKCS5Padding
您可能会发现使用 JAVA_TOOL_OPTIONS 环境变量很有用。比如“export JAVA_TOOL_OPTIONS=”-javaagent:jot.jar=ciphers.jot”。那么无论Java最终如何启动,它都会使用JOT。
如果您想同时使用多个 JOT,您可以将它们全部放入一个大的 .jot 文件中,也可以将多个 .jot 文件放入一个目录中并使用“-javaagent:jot.jar=directory”
最后,每个 JOT 都可以有多个“捕获”。捕获是一种指定要从 JOT 中指定的方法收集哪些数据的方法。简单的捕获如下: #P0 - 第一个参数 #P1 - 第二个参数 #ARGS - 连接在字符串中的所有参数 #OBJ - 在 #RET 上调用方法的对象 #RET - 从方法返回
捕获实际上是 Spring 表达式语言 (SpEL) 表达式,因此您可以调用这些基本对象的方法、比较内容并执行操作。这可以帮助您准确地观察到您想要的内容。有关编写自己的传感器的所有详细信息,请参阅下文。
JOT 使用 FluentLogger,它从java.util.logging
获取配置,并且可以由 JVM 使用-Djava.util.logging.config.file
进行配置
此示例配置会将 JOT 记录到 stdout 以及 /tmp/jot.log
handlers = java.util.logging.ConsoleHandler, java.util.logging.FileHandler
java.util.logging.ConsoleHandler.level = ALL
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
java.util.logging.SimpleFormatter.format = %1$tF %1$tT %4$-7s [%2$s] - %5$s %n
java.util.logging.FileHandler.level=ALL
java.util.logging.FileHandler.pattern=/tmp/jot.log
java.util.logging.FileHandler.formatter=java.util.logging.SimpleFormatter
java.util.logging.FileHandler.append=true
使用:
java -javaagent:jot.jar=ciphers.jot -Djava.util.logging.config.file=/root/jul.properties
传感器是您定义要收集的数据的方式。您还可以使用传感器在应用程序中执行有限的操作。
# The name of this sensor, which can be referenced by reports
- name: "get-ciphers"
# Use this to describe what this sensor does. Try to provide enough
# detail so that anyone would understand what it's about.
description: "What does sensor do?"
# A list of methods to gather data from. To avoid potential performance issues,
# avoid putting sensors in methods that are extremely frequently used,
# like StringBuffer.append() or File.<init>.
methods:
- "a.b.c.Class.method"
- "d.e.f.Class.method"
# Scopes allow you to limit when a sensor will fire.
# A sensor will fire if it is invoked while "inside" one of the scopes. That is,
# when any of these methods is on the stack.
# You can define negative scopes using !scope, so that this sensor will only fire outside the scope
# For static methods you need to prefix the method with "static"
scopes:
- "a.b.c.Class.method" # matches only if inside method
- "!a.b.c.Class.method" # matches only if NOT inside method
- "static a.b.c.Class.method" # matches if inside static method
# Excludes allow you to prevent data from being gathered from any
# classes that starts with any of these patterns or are "isAssignableFrom" these classes
# FIXME: currently you must put .all after each classname
excludes:
- "javax.el.MapELResolver.all"
- "org.foo.package.all"
# Captures are the workhorse of JOT and are written in Spring Expression Language (SpEL)
# You may reference data from the method currently running.
# Options are OBJ, P1..P10, ARGS, RET, STACK. These objects are whatever type they happen
# to be, and you can invoke any existing methods on those objects. Note that STACK is a StackFrame[]
# See https://blog.abelotech.com/posts/useful-things-spring-expression-language-spel/
captures:
- "#RET?.toUpperCase()" # call toUpperCase if #RET is not null (note ?. operator)
- """+#P0+":"+#RET" # for methods that take a name and return a value
- "#OBJ.getCanonicalPath() + " " + #OBJ.exists() ? [EXISTS] : [DOES NOT EXIST]" # use ternary operator
- """+#P0+":"+(#RET ? "Y" : "N")" # for methods that return boolean
# Matchers allow you to filter the data that was captured with a set of regular expressions.
# If there are no matchers then all captures will report data.
# Positive matchers only fire if the data matches the pattern. You'll get a result if any positive matchers match.
# Negative matchers (starting with !) only fire if the data does not match the pattern. You'll get a result if no negative matchers match.
# If you mix positive and negative, you'll get a result if any positive captures match and no negative matchers match.
matchers:
- "^\w*$" # matches anything with word characters start to finish
- "!^\[foo" # matches anything that doesn't start with foo
- "!null" # hide empty results from output
# Exceptions are a way to change the behavior of an application.
# If the sensor fires (capture occurs and matchers fire) then JOT will
# throw a SensorException.
# The message should be appropriate for end user, JOT will log all the relevant details.
# Note that generally you don't want to use #RET in your capture if you're throwing an exception,
# because it will throw at the end of the method, which is probably too late.
exception: "Message"
# Debug mode will generate extra logging for this rule only
debug: "false"
报告可让您定义 JOT 如何随时间收集数据以及如何为您格式化数据。
# Title of this report that will be displayed above the results
- name: "example"
# Type of reports include
# 1. list
# ROWS: caller method
# COLS: one column named after the sensor defined in "cols"
# DATA: values from the sensor named in "cols"
# 2. compare
# ROWS:
# COLS: uses the "cols" sensor values as column headers
# DATA: values from the sensor named in "cols"
# 3. table
# ROWS:
# COLS: uses rule names for cols[0-n] as column headers - parses data values
# DATA: values from the sensor named in "cols"
# 4. series - table but each row starts with a timestamp (currently includes callers col too)
# ROWS:
# COLS: uses rule names for cols[0-n] as column headers - parses data values
# DATA: values from the sensor named in "cols"
type: "table"
# Rows indicates a sensor to be used to populate row headers
rows: "get-routes"
# Cols indicates a sensor (or list of sensors) to be used to populate column headers
cols: "get-users"
# Data indicates how the content of data cells should be populated. Can be a fixed string or a rule name.
data: "X"
应该像克隆这个存储库并使用 Maven 构建一样简单
$ git clone https://github.com/planetlevel/jot.git
$ cd jot
$ mvn install
然后就可以使用目标目录中的jot-xxjar了。
欢迎贡献。如果您想让 JOT 变得更好,请参阅错误跟踪器以查找需要解决的问题。
- Solve problem of sensors inside JOT scope
1) don't instrument JOT classes -- anything using shading
2) use global scope to check anywhere you're inside a sensor call
- Create separate JOT log file instead of using java.util.logging
- New rules
1) which routes are non-idempotent?
- Sensors
# future features - maybe think about reporting?
# enabled: "false"
# sample: "1000" # report every 1000 times? time frequency?
# counter: "?" # report 10 mins?
# scope: include a capture and regex to say whether to start scope (if service.P0.getParameter=foobar)
# exec: run this code. before? after? during?
- Reports
# possible additions to cols -- caller, stack[n], trace#
- Query Language
# investigate using query language instead of yaml jots.
# Perhaps two types of "queries" -- one to create sensors, another to pull reports.
# SELECT #P0.toUpperCase()
FROM java.lang.Runtime.getRuntime.exec
WITHIN javax.servlet.Servlet.service
WHERE pattern
# SELECT [capture, capture]
FROM [method, method]
EXCLUDE [class, class]
WITHIN [scope, scope]
WHERE [pattern, pattern]
THROWS [msg]
# SELECT #P0 FROM javax.crypto.Cipher.getInstance
# SELECT #P0 FROM javax.crypto.Cipher.getInstance
WHERE "DES|DESEDE"
THROWS "Weak encryption algorithm detected"