Add initial InfluxDB v3 support - schema and first function

Co-authored-by: mountaindude <1029262+mountaindude@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2025-09-24 20:25:01 +00:00
committed by Göran Sander
parent c156c746a9
commit 3a0592967c
3 changed files with 221 additions and 1 deletions

View File

@@ -701,6 +701,19 @@ Configuration File:
this.logger.info(
`CONFIG: Influxdb retention policy duration: ${this.config.get('Butler-SOS.influxdbConfig.v2Config.retentionDuration')}`
);
} else if (this.config.get('Butler-SOS.influxdbConfig.version') === 3) {
this.logger.info(
`CONFIG: Influxdb organisation: ${this.config.get('Butler-SOS.influxdbConfig.v3Config.org')}`
);
this.logger.info(
`CONFIG: Influxdb database: ${this.config.get('Butler-SOS.influxdbConfig.v3Config.database')}`
);
this.logger.info(
`CONFIG: Influxdb bucket name: ${this.config.get('Butler-SOS.influxdbConfig.v3Config.bucket')}`
);
this.logger.info(
`CONFIG: Influxdb retention policy duration: ${this.config.get('Butler-SOS.influxdbConfig.v3Config.retentionDuration')}`
);
} else {
this.logger.error(
`CONFIG: Influxdb version ${this.config.get('Butler-SOS.influxdbConfig.version')} is not supported!`
@@ -863,13 +876,28 @@ Configuration File:
const token = this.config.get('Butler-SOS.influxdbConfig.v2Config.token');
try {
this.influx = new InfluxDB2({ url, token });
this.influx = new InfluxDB({ url, token });
} catch (err) {
this.logger.error(
`INFLUXDB2 INIT: Error creating InfluxDB 2 client: ${this.getErrorMessage(err)}`
);
this.logger.error(`INFLUXDB2 INIT: Exiting.`);
}
} else if (this.config.get('Butler-SOS.influxdbConfig.version') === 3) {
// Set up Influxdb v3 client (uses same client library as v2)
const url = `http://${this.config.get('Butler-SOS.influxdbConfig.host')}:${this.config.get(
'Butler-SOS.influxdbConfig.port'
)}`;
const token = this.config.get('Butler-SOS.influxdbConfig.v3Config.token');
try {
this.influx = new InfluxDB({ url, token });
} catch (err) {
this.logger.error(
`INFLUXDB3 INIT: Error creating InfluxDB 3 client: ${this.getErrorMessage(err)}`
);
this.logger.error(`INFLUXDB3 INIT: Exiting.`);
}
} else {
this.logger.error(
`CONFIG: Influxdb version ${this.config.get('Butler-SOS.influxdbConfig.version')} is not supported!`
@@ -1114,6 +1142,78 @@ Configuration File:
}
});
}
} else if (this.config.get('Butler-SOS.influxdbConfig.version') === 3) {
// Get config
const org = this.config.get('Butler-SOS.influxdbConfig.v3Config.org');
const database = this.config.get('Butler-SOS.influxdbConfig.v3Config.database');
const bucketName = this.config.get('Butler-SOS.influxdbConfig.v3Config.bucket');
const description = this.config.get('Butler-SOS.influxdbConfig.v3Config.description');
const token = this.config.get('Butler-SOS.influxdbConfig.v3Config.token');
const retentionDuration = this.config.get(
'Butler-SOS.influxdbConfig.v3Config.retentionDuration'
);
if (
this.influx &&
this.config.get('Butler-SOS.influxdbConfig.enable') === true &&
org?.length > 0 &&
database?.length > 0 &&
bucketName?.length > 0 &&
token?.length > 0 &&
retentionDuration?.length > 0
) {
enableInfluxdb = true;
}
if (enableInfluxdb) {
// For InfluxDB v3, we use the database directly
this.logger.info(
`INFLUXDB3: Using organization "${org}" with database "${database}"`
);
// Create array of per-server writeAPI objects for v3
// Each object has two properties: host and writeAPI, where host can be used as key later on
this.serverList.forEach((server) => {
// Get per-server tags
const tags = getServerTags(this.logger, server);
// advanced write options for InfluxDB v3
const writeOptions = {
/* default tags to add to every point */
defaultTags: tags,
/* maximum time in millis to keep points in an unflushed batch, 0 means don't periodically flush */
flushInterval: 5000,
/* the count of internally-scheduled retries upon write failure, the delays between write attempts follow an exponential backoff strategy if there is no Retry-After HTTP header */
maxRetries: 2, // do not retry writes
// ... there are more write options that can be customized, see
// https://influxdata.github.io/influxdb-client-js/influxdb-client.writeoptions.html and
// https://influxdata.github.io/influxdb-client-js/influxdb-client.writeretryoptions.html
};
try {
// For InfluxDB v3, we use database instead of bucket
const serverWriteApi = this.influx.getWriteApi(
org,
database,
'ns',
writeOptions
);
// Save to global variable, using serverName as key
this.influxWriteApi.push({
serverName: server.serverName,
writeAPI: serverWriteApi,
});
} catch (err) {
this.logger.error(
`INFLUXDB3: Error getting write API: ${this.getErrorMessage(err)}`
);
}
});
}
}
}

View File

@@ -316,6 +316,19 @@ export const destinationsSchema = {
},
port: { type: 'number' },
version: { type: 'number' },
v3Config: {
type: 'object',
properties: {
org: { type: 'string' },
bucket: { type: 'string' },
database: { type: 'string' },
description: { type: 'string' },
token: { type: 'string' },
retentionDuration: { type: 'string' },
},
required: ['org', 'bucket', 'database', 'description', 'token', 'retentionDuration'],
additionalProperties: false,
},
v2Config: {
type: 'object',
properties: {

View File

@@ -546,6 +546,113 @@ export async function postHealthMetricsToInfluxdb(serverName, host, body, server
`HEALTH METRICS: Error saving health data to InfluxDB v2! ${globals.getErrorMessage(err)}`
);
}
} else if (globals.config.get('Butler-SOS.influxdbConfig.version') === 3) {
// Only write to InfluxDB if the global influxWriteApi object has been initialized
if (!globals.influxWriteApi) {
globals.logger.warn(
'HEALTH METRICS: Influxdb write API object not initialized. Data will not be sent to InfluxDB'
);
return;
}
// Find writeApi for the server specified by serverName
const writeApi = globals.influxWriteApi.find(
(element) => element.serverName === serverName
);
// Ensure that the writeApi object was found
if (!writeApi) {
globals.logger.warn(
`HEALTH METRICS: Influxdb write API object not found for host ${host}. Data will not be sent to InfluxDB`
);
return;
}
// Create a new point with the data to be written to InfluxDB v3
const points = [
new Point('sense_server')
.stringField('version', body.version)
.stringField('started', body.started)
.stringField('uptime', formattedTime),
new Point('mem')
.floatField('comitted', body.mem.committed)
.floatField('allocated', body.mem.allocated)
.floatField('free', body.mem.free),
new Point('apps')
.intField('active_docs_count', body.apps.active_docs.length)
.intField('loaded_docs_count', body.apps.loaded_docs.length)
.intField('in_memory_docs_count', body.apps.in_memory_docs.length)
.stringField(
'active_docs',
globals.config.get('Butler-SOS.influxdbConfig.includeFields.activeDocs')
? body.apps.active_docs
: ''
)
.stringField(
'active_docs_names',
globals.config.get('Butler-SOS.appNames.enableAppNameExtract') &&
globals.config.get('Butler-SOS.influxdbConfig.includeFields.activeDocs')
? activeSessionDocNames
: ''
)
.stringField(
'loaded_docs',
globals.config.get('Butler-SOS.influxdbConfig.includeFields.loadedDocs')
? body.apps.loaded_docs
: ''
)
.stringField(
'loaded_docs_names',
globals.config.get('Butler-SOS.appNames.enableAppNameExtract') &&
globals.config.get('Butler-SOS.influxdbConfig.includeFields.loadedDocs')
? loadedSessionDocNames
: ''
)
.stringField(
'in_memory_docs',
globals.config.get('Butler-SOS.influxdbConfig.includeFields.inMemoryDocs')
? body.apps.in_memory_docs
: ''
)
.stringField(
'in_memory_docs_names',
globals.config.get('Butler-SOS.appNames.enableAppNameExtract') &&
globals.config.get('Butler-SOS.influxdbConfig.includeFields.inMemoryDocs')
? inMemorySessionDocNames
: ''
)
.intField('calls', body.apps.calls)
.intField('selections', body.apps.selections),
new Point('cpu').intField('total', body.cpu.total),
new Point('session')
.intField('active', body.session.active)
.intField('total', body.session.total),
new Point('users')
.intField('active', body.users.active)
.intField('total', body.users.total),
new Point('cache')
.intField('hits', body.cache.hits)
.intField('lookups', body.cache.lookups)
.intField('added', body.cache.added)
.intField('replaced', body.cache.replaced)
.intField('bytes_added', body.cache.bytes_added),
];
// Write to InfluxDB
try {
const res = await writeApi.writeAPI.writePoints(points);
globals.logger.debug(`HEALTH METRICS: Wrote data to InfluxDB v3`);
} catch (err) {
globals.logger.error(
`HEALTH METRICS: Error saving health data to InfluxDB v3! ${globals.getErrorMessage(err)}`
);
}
}
}