Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[jmxreceiver] add support for running custom Apache Groovy script at runtime #35933

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion receiver/jmxreceiver/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,11 @@ receivers:
jmx:
jar_path: /opt/opentelemetry-java-contrib-jmx-metrics.jar
endpoint: my_jmx_host:12345
target_system: jvm
# you can use pre-defined groovy scripts via one or more target systems
target_system: jvm,kafka
# and/or you can use your own groovy script to gather more specific metrics
# that might not be supported by existing target systems
groovy_script: /some_script.groovy
collection_interval: 10s
initial_delay: 1s
# optional: the same as specifying OTLP receiver endpoint.
Expand Down Expand Up @@ -90,6 +94,16 @@ go build -ldflags "-X github.com/open-telemetry/opentelemetry-collector-contrib/

Corresponds to the `otel.jmx.target.system` property.

### groovy_script

Target systems pre-define groovy scripts for you. Sometimes you need to gather metrics
that are not defined by existing target systems. You can specify a path to a groovy
script that will be loaded by itself or alongside your target system groovy scripts at
runtime. Make sure the groovy script is indeed available at the provided path at
runtime.

Corresponds to the `otel.jmx.groovy.script` property.

### collection_interval (default: `10s`)

The interval time for the Groovy script to be run and metrics to be exported by the JMX Metric Gatherer within the persistent JRE process.
Expand Down
16 changes: 10 additions & 6 deletions receiver/jmxreceiver/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@ type Config struct {
JARPath string `mapstructure:"jar_path"`
// The Service URL or host:port for the target coerced to one of form: service:jmx:rmi:///jndi/rmi://<host>:<port>/jmxrmi.
Endpoint string `mapstructure:"endpoint"`
// The target system for the metric gatherer whose built in groovy script to run.
// The target system for the metric gatherer whose built in groovy script to run (optional if using a groovy script)
TargetSystem string `mapstructure:"target_system"`
// Path to the groovy script to run (optional if using target system(s))
GroovyScript string `mapstructure:"groovy_script"`
// The duration in between groovy script invocations and metric exports (10 seconds by default).
// Will be converted to milliseconds.
CollectionInterval time.Duration `mapstructure:"collection_interval"`
Expand Down Expand Up @@ -223,8 +225,8 @@ func (c *Config) Validate() error {
if c.Endpoint == "" {
missingFields = append(missingFields, "`endpoint`")
}
if c.TargetSystem == "" {
missingFields = append(missingFields, "`target_system`")
if c.TargetSystem == "" && c.GroovyScript == "" {
missingFields = append(missingFields, "`target_system` or `groovy_script`")
}
if missingFields != nil {
return fmt.Errorf("missing required field(s): %v", strings.Join(missingFields, ", "))
Expand Down Expand Up @@ -257,9 +259,11 @@ func (c *Config) Validate() error {
}
}

for _, system := range strings.Split(c.TargetSystem, ",") {
if _, ok := validTargetSystems[strings.ToLower(system)]; !ok {
return fmt.Errorf("`target_system` list may only be a subset of %s", listKeys(validTargetSystems))
if c.TargetSystem != "" {
for _, system := range strings.Split(c.TargetSystem, ",") {
if _, ok := validTargetSystems[strings.ToLower(system)]; !ok {
return fmt.Errorf("`target_system` list may only be a subset of %s", listKeys(validTargetSystems))
}
}
}

Expand Down
7 changes: 4 additions & 3 deletions receiver/jmxreceiver/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ func TestLoadConfig(t *testing.T) {
JARPath: "testdata/fake_jmx.jar",
Endpoint: "myendpoint:12345",
TargetSystem: "jvm",
GroovyScript: "/some_script.groovy",
CollectionInterval: 15 * time.Second,
Username: "myusername",
Password: "mypassword",
Expand Down Expand Up @@ -84,8 +85,8 @@ func TestLoadConfig(t *testing.T) {
},
},
{
id: component.NewIDWithName(metadata.Type, "missingtarget"),
expectedErr: "missing required field(s): `target_system`",
id: component.NewIDWithName(metadata.Type, "missingtargetandgroovyscript"),
expectedErr: "missing required field(s): `target_system` or `groovy_script`",
expected: &Config{
JARPath: "testdata/fake_jmx.jar",
Endpoint: "service:jmx:rmi:///jndi/rmi://host:12345/jmxrmi",
Expand Down Expand Up @@ -323,7 +324,7 @@ func TestWithInvalidConfig(t *testing.T) {
require.NotNil(t, cfg)

err := cfg.Validate()
assert.Equal(t, "missing required field(s): `endpoint`, `target_system`", err.Error())
assert.Equal(t, "missing required field(s): `endpoint`, `target_system` or `groovy_script`", err.Error())
}

func mockJarVersions() {
Expand Down
1 change: 1 addition & 0 deletions receiver/jmxreceiver/receiver.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ func (jmx *jmxMetricReceiver) buildJMXMetricGathererConfig() (string, error) {
config["otel.jmx.service.url"] = jmx.config.Endpoint
config["otel.jmx.interval.milliseconds"] = strconv.FormatInt(jmx.config.CollectionInterval.Milliseconds(), 10)
config["otel.jmx.target.system"] = jmx.config.TargetSystem
config["otel.jmx.groovy.script"] = jmx.config.GroovyScript

endpoint := jmx.config.OTLPExporterConfig.Endpoint
if !strings.HasPrefix(endpoint, "http") {
Expand Down
3 changes: 3 additions & 0 deletions receiver/jmxreceiver/receiver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ func TestBuildJMXMetricGathererConfig(t *testing.T) {
&Config{
Endpoint: "myhost:12345",
TargetSystem: "mytargetsystem",
GroovyScript: "/some_script.groovy",
CollectionInterval: 123 * time.Second,
OTLPExporterConfig: otlpExporterConfig{
Endpoint: "https://myotlpendpoint",
Expand Down Expand Up @@ -85,6 +86,7 @@ javax.net.ssl.trustStoreType = ASCII
otel.exporter.otlp.endpoint = https://myotlpendpoint
otel.exporter.otlp.headers = one=two,three=four
otel.exporter.otlp.timeout = 234000
otel.jmx.groovy.script = /some_script.groovy
otel.jmx.interval.milliseconds = 123000
otel.jmx.password = mypass \nword
otel.jmx.realm = myrealm
Expand All @@ -101,6 +103,7 @@ otel.resource.attributes = abc=123,one=two`,
&Config{
Endpoint: "myhostwithoutport",
TargetSystem: "mytargetsystem",
GroovyScript: "/some_script.groovy",
CollectionInterval: 123 * time.Second,
OTLPExporterConfig: otlpExporterConfig{
Endpoint: "myotlpendpoint",
Expand Down
3 changes: 2 additions & 1 deletion receiver/jmxreceiver/testdata/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ jmx/all:
jar_path: testdata/fake_jmx.jar
endpoint: myendpoint:12345
target_system: jvm
groovy_script: /some_script.groovy
collection_interval: 15s
username: myusername
password: mypassword
Expand All @@ -27,7 +28,7 @@ jmx/all:
jmx/missingendpoint:
jar_path: testdata/fake_jmx.jar
target_system: jvm
jmx/missingtarget:
jmx/missingtargetandgroovyscript:
jar_path: testdata/fake_jmx.jar
endpoint: service:jmx:rmi:///jndi/rmi://host:12345/jmxrmi
jmx/invalidinterval:
Expand Down