99import com .aws .greengrass .config .Topics ;
1010import com .aws .greengrass .dependency .ImplementsService ;
1111import com .aws .greengrass .dependency .State ;
12+ import com .aws .greengrass .deployment .DeviceConfiguration ;
1213import com .aws .greengrass .lifecyclemanager .PluginService ;
1314import com .aws .greengrass .telemetry .impl .Metric ;
15+ import com .aws .greengrass .telemetry .nucleus .emitter .emf .EmfFileWriter ;
1416import com .aws .greengrass .telemetry .nucleus .emitter .metrics .KernelMetricsEmitter ;
1517import com .aws .greengrass .telemetry .nucleus .emitter .metrics .SystemMetricsEmitter ;
1618import com .aws .greengrass .telemetry .nucleus .emitter .publisher .MqttPublisher ;
1719import com .aws .greengrass .telemetry .nucleus .emitter .publisher .PubSubPublisher ;
20+ import com .aws .greengrass .util .Coerce ;
1821import com .aws .greengrass .util .SerializerFactory ;
1922import com .aws .greengrass .util .Utils ;
2023import com .fasterxml .jackson .core .JsonProcessingException ;
2124import com .fasterxml .jackson .databind .ObjectMapper ;
2225import lombok .AccessLevel ;
2326import lombok .Getter ;
2427
28+ import java .nio .file .Paths ;
2529import java .util .Collection ;
2630import java .util .Collections ;
2731import java .util .List ;
@@ -64,6 +68,8 @@ public class NucleusEmitter extends PluginService {
6468 //Metric publishers
6569 private final PubSubPublisher pubSubPublisher ;
6670 private final MqttPublisher mqttPublisher ;
71+ // volatile ensures visibility across config subscriber and publish threads
72+ private volatile EmfFileWriter emfFileWriter ;
6773
6874 private final ChildChanged subscribeToConfigChanges = (what , topic ) ->
6975 handleConfiguration (this .config .lookupTopics (CONFIGURATION_CONFIG_KEY ));
@@ -108,9 +114,14 @@ private void handleConfiguration(Topics configurationTopics) {
108114 .equals (newConfiguration .getExcludeMounts ());
109115 boolean excludeInterfacesChanged = !configuration .getExcludeInterfaces ()
110116 .equals (newConfiguration .getExcludeInterfaces ());
117+ boolean outputModeChanged = !configuration .getOutputMode ()
118+ .equals (newConfiguration .getOutputMode ());
119+ boolean outputDirectoryChanged = !configuration .getOutputDirectory ()
120+ .equals (newConfiguration .getOutputDirectory ());
111121
112122 if (!pubSubPublishChanged && !mqttTopicChanged && !telemetryPublishIntervalMsChanged
113- && !metricsLevelChanged && !excludeMountsChanged && !excludeInterfacesChanged ) {
123+ && !metricsLevelChanged && !excludeMountsChanged && !excludeInterfacesChanged
124+ && !outputModeChanged && !outputDirectoryChanged ) {
114125 return ;
115126 }
116127
@@ -138,6 +149,14 @@ private void handleConfiguration(Topics configurationTopics) {
138149 newConfiguration .getExcludeMounts (),
139150 newConfiguration .getExcludeInterfaces ());
140151 }
152+ if (outputModeChanged || outputDirectoryChanged ) {
153+ if (newConfiguration .isEmfEnabled ()) {
154+ this .emfFileWriter = new EmfFileWriter (getThingName (),
155+ Paths .get (newConfiguration .getOutputDirectory ()));
156+ } else {
157+ this .emfFileWriter = null ; // NOPMD NullAssignment - disables EMF output
158+ }
159+ }
141160 scheduleTelemetryPublish ();
142161 }
143162
@@ -151,16 +170,36 @@ public void startup() {
151170 scheduleTelemetryPublish ();
152171 }
153172
154- private void publishTelemetry (boolean pubSubPublish , String pubSubTopic , boolean mqttPublish , String mqttTopic ) {
155- String jsonString = retrieveMetricsJson (jsonMapper );
156- if (pubSubPublish ) {
157- this .pubSubPublisher .publishMessage (jsonString , pubSubTopic );
173+ private void publishTelemetry (boolean pubSubPublish , String pubSubTopic ,
174+ boolean mqttPublish , String mqttTopic ) {
175+ List <Metric > metrics = collectMetrics ();
176+ if (pubSubPublish || mqttPublish ) {
177+ String jsonString = null ; // NOPMD - set in try, checked before use
178+ try {
179+ jsonString = jsonMapper .writeValueAsString (metrics );
180+ } catch (JsonProcessingException e ) {
181+ logger .error (JSON_PARSE_ERROR_LOG , e );
182+ }
183+ if (pubSubPublish && jsonString != null ) {
184+ this .pubSubPublisher .publishMessage (jsonString , pubSubTopic );
185+ }
186+ if (mqttPublish && jsonString != null ) {
187+ this .mqttPublisher .publishMessage (jsonString , mqttTopic );
188+ }
158189 }
159- if (mqttPublish ) {
160- this .mqttPublisher .publishMessage (jsonString , mqttTopic );
190+ EmfFileWriter localEmf = this .emfFileWriter ;
191+ if (localEmf != null ) {
192+ localEmf .write (metrics );
161193 }
162194 }
163195
196+ private List <Metric > collectMetrics () {
197+ SystemMetricsEmitter localSme = this .sme ;
198+ return Stream .of (localSme .getMetrics (), kme .getMetrics ())
199+ .flatMap (Collection ::stream )
200+ .collect (Collectors .toList ());
201+ }
202+
164203 private void scheduleTelemetryPublish () {
165204 final NucleusEmitterConfiguration configuration = currentConfiguration .get ();
166205 final boolean newPubPublish = configuration .isPubsubPublish ();
@@ -192,13 +231,9 @@ private void scheduleTelemetryPublish() {
192231 }
193232
194233 protected String retrieveMetricsJson (ObjectMapper jsonMapper ) {
195-
196234 String jsonString = null ;
197235 try {
198- SystemMetricsEmitter localSme = this .sme ;
199- List <Metric > metrics = Stream .of (localSme .getMetrics (), kme .getMetrics ())
200- .flatMap (Collection ::stream )
201- .collect (Collectors .toList ());
236+ List <Metric > metrics = collectMetrics ();
202237 jsonString = jsonMapper .writeValueAsString (metrics );
203238 } catch (JsonProcessingException e ) {
204239 logger .error (JSON_PARSE_ERROR_LOG , e );
@@ -209,6 +244,13 @@ protected String retrieveMetricsJson(ObjectMapper jsonMapper) {
209244 @ Override
210245 public void shutdown () {
211246 cancelJob (telemetryPublishFuture , telemetryPublishInProgressLock , true );
247+ this .emfFileWriter = null ; // NOPMD NullAssignment - release on shutdown
248+ }
249+
250+ private String getThingName () {
251+ return Coerce .toString (
252+ this .context .get (DeviceConfiguration .class )
253+ .getThingName ());
212254 }
213255
214256 private void cancelJob (ScheduledFuture <?> future , Object lock , boolean immediately ) {
0 commit comments