@@ -37,7 +37,7 @@ import filodb.core.GlobalConfig
3737case class OTelMetricsConfig (exportIntervalSeconds : Int = 60 ,
3838 resourceAttributes : Map [String , String ],
3939 otlpHeaders : Map [String , String ],
40- exporterType : String , // "otlp" or "log"
40+ exporterFactoryClassName : String , // Fully qualified class name of MetricExporterFactory
4141 exponentialHistogram : Boolean ,
4242 customHistogramBucketsTime : List [Double ], // used only if exponentialHistogram=false
4343 customHistogramBuckets : List [Double ], // used only if exponentialHistogram=false
@@ -46,14 +46,79 @@ case class OTelMetricsConfig(exportIntervalSeconds: Int = 60,
4646 otlpClientCertPath : Option [String ],
4747 otlpClientKeyPath : Option [String ])
4848
49+ /**
50+ * Factory interface for creating MetricExporter instances
51+ */
52+ trait MetricExporterFactory {
53+ /**
54+ * Creates a MetricExporter instance
55+ * @param config The OTelMetricsConfig to use for configuration
56+ * @return A configured MetricExporter
57+ */
58+ def create (config : OTelMetricsConfig ): MetricExporter
59+ }
60+
61+ /**
62+ * Factory for creating OtlpGrpcMetricExporter instances
63+ */
64+ class OtlpGrpcMetricExporterFactory extends MetricExporterFactory {
65+ override def create (config : OTelMetricsConfig ): MetricExporter = {
66+ val b = OtlpGrpcMetricExporter .builder()
67+ .setAggregationTemporalitySelector(AggregationTemporalitySelector .deltaPreferred())
68+ .setEndpoint(config.otlpEndpoint.getOrElse(
69+ throw new IllegalArgumentException (" otlp-endpoint must be configured when using OTLP exporter" )))
70+
71+ if (config.otlpTrustedCertsPath.isDefined) {
72+ b.setTrustedCertificates(Files .readAllBytes(Paths .get(config.otlpTrustedCertsPath.get)))
73+ }
74+ if (config.otlpClientKeyPath.isDefined && config.otlpClientCertPath.isDefined) {
75+ b.setClientTls(Files .readAllBytes(Paths .get(config.otlpClientKeyPath.get)),
76+ Files .readAllBytes(Paths .get(config.otlpClientCertPath.get)))
77+ }
78+ config.otlpHeaders.foreach { case (key, value) =>
79+ b.addHeader(key, value)
80+ }
81+ b.build()
82+ }
83+ }
84+
85+ /**
86+ * Factory for creating OtlpJsonLoggingMetricExporter instances
87+ */
88+ class LogMetricExporterFactory extends MetricExporterFactory {
89+ override def create (config : OTelMetricsConfig ): MetricExporter = {
90+ OtlpJsonLoggingMetricExporter .create(AggregationTemporality .DELTA )
91+ }
92+ }
93+
4994object OTelMetricsConfig {
50- def fromConfig (metricsConfig : Config ): OTelMetricsConfig = {
5195
96+ /**
97+ * Creates a MetricExporterFactory instance using reflection
98+ * @param className Fully qualified class name of the factory
99+ * @return Instance of MetricExporterFactory
100+ */
101+ def instantiateExporterFactory (className : String ): MetricExporterFactory = {
102+ try {
103+ val clazz = Class .forName(className)
104+ clazz.getDeclaredConstructor().newInstance().asInstanceOf [MetricExporterFactory ]
105+ } catch {
106+ case e : ClassNotFoundException =>
107+ throw new IllegalArgumentException (s " Factory class not found: $className" , e)
108+ case e : ClassCastException =>
109+ throw new IllegalArgumentException (
110+ s " Class $className does not implement MetricExporterFactory trait " , e)
111+ case e : Exception =>
112+ throw new IllegalArgumentException (s " Failed to instantiate factory: $className" , e)
113+ }
114+ }
115+
116+ def fromConfig (metricsConfig : Config ): OTelMetricsConfig = {
52117 OTelMetricsConfig (
53118 otlpEndpoint = metricsConfig.as[Option [String ]](" otlp-endpoint" ),
54119 exportIntervalSeconds = metricsConfig.as[Int ](" export-interval-seconds" ),
55120 resourceAttributes = metricsConfig.as[Map [String , String ]](" resource-attributes" ),
56- exporterType = metricsConfig.as[String ](" exporter-type " ),
121+ exporterFactoryClassName = metricsConfig.as[String ](" exporter-factory-class-name " ),
57122 exponentialHistogram = metricsConfig.as[Boolean ](" exponential-histogram" ),
58123 customHistogramBucketsTime = metricsConfig.as[List [Double ]](" custom-histogram-buckets-time" ).sorted,
59124 customHistogramBuckets = metricsConfig.as[List [Double ]](" custom-histogram-buckets" ).sorted,
@@ -173,30 +238,10 @@ private class FilodbMetrics(filodbMetricsConfig: Config) extends StrictLogging {
173238 }
174239 val resource = resourceBuilder.build()
175240
176- // Create exporter based on configuration
177- val metricExporter : MetricExporter = otelConfig.exporterType.toLowerCase match {
178- case " log" =>
179- logger.info(" Using log-based metrics exporter" )
180- OtlpJsonLoggingMetricExporter .create(AggregationTemporality .DELTA )
181- case " otlp" =>
182- logger.info(s " Using OTLP metrics exporter with endpoint: ${otelConfig.otlpEndpoint}" )
183- val b = OtlpGrpcMetricExporter .builder()
184- .setAggregationTemporalitySelector(AggregationTemporalitySelector .deltaPreferred())
185- .setEndpoint(otelConfig.otlpEndpoint.get)
186-
187- if (otelConfig.otlpTrustedCertsPath.isDefined) {
188- b.setTrustedCertificates(Files .readAllBytes(Paths .get(otelConfig.otlpTrustedCertsPath.get)))
189- }
190- if (otelConfig.otlpClientKeyPath.isDefined && otelConfig.otlpClientCertPath.isDefined) {
191- b.setClientTls(Files .readAllBytes(Paths .get(otelConfig.otlpClientKeyPath.get)),
192- Files .readAllBytes(Paths .get(otelConfig.otlpClientCertPath.get)))
193- }
194- otelConfig.otlpHeaders.foreach { case (key, value) =>
195- b.addHeader(key, value)
196- }
197- b.build()
198- case _ => throw new IllegalArgumentException (s " Unknown exporter type: ${otelConfig.exporterType}" )
199- }
241+ // Create exporter using the configured factory
242+ logger.info(s " Using ${otelConfig.exporterFactoryClassName} metrics exporter " )
243+ val metricExporter : MetricExporter =
244+ OTelMetricsConfig .instantiateExporterFactory(otelConfig.exporterFactoryClassName).create(otelConfig)
200245
201246 // Create periodic metric reader with delta aggregation
202247 val metricReader = PeriodicMetricReader .builder(metricExporter)
0 commit comments