Le Java Observability Toolkit (JOT) est une plate-forme permettant de rendre n'importe quelle application Java observable sans écrire de code ni même recompiler. La plateforme est composée de...
Vous pouvez créer des JOT rapidement et facilement sans aucun codage. Les JOT peuvent rendre observable à peu près tout ce qui se passe dans une application Java en cours d'exécution. Voici quelques exemples de l'utilisation que vous pourriez en faire... mais la seule limite est votre créativité.
Vous êtes bloqué pour résoudre un problème de production et vous ne disposez pas de suffisamment de journaux ni de la possibilité de déboguer. Avec une simple règle JOT, vous pouvez obtenir les données dont vous avez besoin rapidement et facilement.
Vous souhaitez surveiller quelque chose qui n'est accessible qu'au plus profond d'une application et vous n'avez aucun moyen de l'exposer à vos outils.
Vous souhaitez empêcher l'utilisation abusive de méthodes puissantes par des attaquants, mais celles-ci se trouvent dans des bibliothèques que vous ne contrôlez pas.
L'objectif de JOT est de fournir une puissance d'instrumentation maximale sans compromettre la simplicité.
Faites-nous savoir les choses sympas que vous faites avec JOT ! Et pensez à les contribuer au projet afin que d'autres puissent partager votre génialité. Le moyen le plus simple de contribuer est de créer une pull request. Si vous partagez un JOT, ajoutez-le au répertoire "contrib" des projets... et veuillez inclure un commentaire détaillant à quoi il est destiné à être utilisé.
Définissez vos capteurs et rapports dans yaml et enregistrez-les dans un fichier .jot. Les capteurs définissent les données à capturer et les « rapports » indiquent à JOT comment suivre les données au fil du temps.
sensors:
- name: "get-ciphers"
description: "Identifies encryption ciphers"
methods:
- "javax.crypto.Cipher.getInstance"
captures:
- "#P0"
reports:
- name: "Encryption Usage"
type: "list"
cols: "get-ciphers"
En gros, tout ce que vous avez à faire est d'ajouter JOT à la JVM
Ensuite, utilisez simplement votre application normalement et laissez JOT collecter des données pour vous. JOT créera un joli tableau capturant exactement où le cryptage est utilisé et quel algorithme est spécifié.
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
L'utilisation de la variable d'environnement JAVA_TOOL_OPTIONS pourrait s'avérer utile. Comme "export JAVA_TOOL_OPTIONS="-javaagent:jot.jar=ciphers.jot". Ensuite, quelle que soit la façon dont Java sera finalement lancé, il utilisera JOT.
Si vous souhaitez utiliser plusieurs JOT en même temps, vous pouvez soit les mettre tous dans un seul gros fichier .jot, soit placer plusieurs fichiers .jot dans un répertoire et utiliser "-javaagent:jot.jar=directory".
Enfin, chaque JOT peut avoir plusieurs « captures ». Une capture est un moyen de spécifier les données que vous souhaitez collecter à partir des méthodes que vous avez spécifiées dans le JOT. Les captures simples sont des choses comme : #P0 - le premier paramètre #P1 - le deuxième paramètre #ARGS - tous les paramètres concaténés dans une chaîne #OBJ - l'objet sur lequel la méthode est appelée #RET - le retour de la méthode
Les captures sont en fait des expressions Spring Expression Language (SpEL), vous pouvez donc appeler des méthodes sur ces objets de base, comparer des éléments et effectuer des opérations. Cela vous aide à observer exactement ce que vous voulez. Voir ci-dessous pour tous les détails sur l'écriture de vos propres capteurs.
JOT utilise FluentLogger qui prend la configuration de java.util.logging
et peut être configuré par la JVM avec -Djava.util.logging.config.file
Cet exemple de configuration enregistrera JOT sur la sortie standard ainsi que sur /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
en utilisant:
java -javaagent:jot.jar=ciphers.jot -Djava.util.logging.config.file=/root/jul.properties
Les capteurs vous permettent de définir les données que vous souhaitez collecter. Vous pouvez également utiliser des capteurs pour effectuer des actions limitées au sein d'une application.
# 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"
Les rapports vous permettent de définir comment JOT collectera les données au fil du temps et comment il les formatera pour vous.
# 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"
Cela devrait être aussi simple que de cloner ce dépôt et de le construire avec maven
$ git clone https://github.com/planetlevel/jot.git
$ cd jot
$ mvn install
Ensuite, vous pouvez utiliser le jot-xxjar dans le répertoire cible.
Les contributions sont les bienvenues. Consultez le bugtracker pour trouver les problèmes sur lesquels travailler si vous souhaitez améliorer 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"