O Java Observability Toolkit (JOT) é uma plataforma para tornar qualquer aplicativo Java observável sem escrever nenhum código ou mesmo recompilar. A plataforma consiste em...
Você pode criar JOTs de forma rápida e fácil, sem qualquer codificação. JOTs podem tornar observável praticamente qualquer coisa em um aplicativo Java em execução. Aqui estão alguns exemplos de como você pode usá-lo... mas o único limite é a sua criatividade.
Você está preso na solução de um problema de produção e não tem logs suficientes ou capacidade de depuração. Com uma regra JOT simples, você pode obter os dados necessários de forma rápida e fácil.
Você deseja monitorar algo que só pode ser acessado no interior de um aplicativo e não tem como expô-lo às suas ferramentas.
Você deseja evitar o abuso de métodos poderosos por invasores, mas eles estão em bibliotecas que você não controla.
O objetivo do JOT é fornecer potência máxima de instrumentação sem comprometer a simplicidade.
Conte-nos as coisas legais que você faz com o JOT! E considere contribuir com eles de volta para o projeto para que outros possam compartilhar sua grandiosidade. A maneira mais fácil de contribuir é criar uma solicitação pull. Se você estiver compartilhando um JOT, adicione-o ao diretório "contrib" do projeto... e inclua um comentário detalhando para que ele deve ser usado.
Defina seus sensores e relatórios em yaml e salve-os em um arquivo .jot. Os sensores definem quais dados capturar e os “relatórios” informam ao JOT como rastrear os dados ao longo do tempo.
sensors:
- name: "get-ciphers"
description: "Identifies encryption ciphers"
methods:
- "javax.crypto.Cipher.getInstance"
captures:
- "#P0"
reports:
- name: "Encryption Usage"
type: "list"
cols: "get-ciphers"
Basicamente tudo que você precisa fazer é adicionar JOT à JVM
Depois é só usar seu aplicativo normalmente e deixar o JOT coletar dados para você. JOT criará uma bela tabela capturando exatamente onde a criptografia é usada e qual algoritmo é especificado.
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
Você pode achar útil usar a variável de ambiente JAVA_TOOL_OPTIONS. Como "export JAVA_TOOL_OPTIONS="-javaagent:jot.jar=ciphers.jot".Então, não importa como o Java seja lançado, ele usará JOT.
Se quiser usar vários JOTs ao mesmo tempo, você pode colocá-los todos em um grande arquivo .jot ou pode colocar vários arquivos .jot em um diretório e usar "-javaagent:jot.jar=directory"
Por último, cada JOT pode ter múltiplas “capturas”. Uma captura é uma forma de especificar quais dados você deseja coletar dos métodos especificados no JOT. As capturas simples são coisas como: #P0 - o primeiro parâmetro #P1 - o segundo parâmetro #ARGS - todos os parâmetros concatenados em uma String #OBJ - o objeto no qual o método está sendo chamado #RET - o retorno do método
As capturas são, na verdade, expressões Spring Expression Language (SpEL), então você pode chamar métodos nesses objetos básicos, comparar coisas e realizar operações. Isso ajuda você a observar exatamente o que deseja. Veja abaixo todos os detalhes sobre como escrever seus próprios sensores.
JOT usa FluentLogger que obtém configuração de java.util.logging
e pode ser configurado pela JVM com -Djava.util.logging.config.file
Este exemplo de configuração registrará JOT em stdout e também em /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
usando:
java -javaagent:jot.jar=ciphers.jot -Djava.util.logging.config.file=/root/jul.properties
Os sensores são como você define os dados que deseja coletar. Você também pode usar sensores para executar ações limitadas em um aplicativo.
# 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"
Os relatórios permitem definir como o JOT coletará dados ao longo do tempo e como os formatará para você.
# 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"
Deve ser tão simples quanto clonar este repositório e construir com maven
$ git clone https://github.com/planetlevel/jot.git
$ cd jot
$ mvn install
Então você pode usar jot-xxjar no diretório de destino.
Contribuições são bem-vindas. Consulte o bugtracker para encontrar problemas nos quais trabalhar se quiser melhorar o 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"