@@ -6,33 +6,21 @@ import java.net.URI
66import java .time .Instant
77import play .api .{Logger , Play }
88import play .api .Play .current
9- import play .api .libs .json .{JsValue , Json }
9+ import play .api .libs .json .{JsObject , JsValue , Json }
1010
1111object EventSinkService {
1212 val EXCHANGE_NAME_CONFIG_KEY = " eventsink.exchangename"
1313 val QUEUE_NAME_CONFIG_KEY = " eventsink.queuename"
1414
1515 val EXCHANGE_NAME_DEFAULT_VALUE = " clowder.metrics"
16- val QUEUE_NAME_DEFAULT_VALUE = " event.sink"
17-
18- // TODO: Make sure these match the real config key names
19- val AMPLITUDE_CONFIG_KEY = " amplitude.apikey"
20- val GA_CONFIG_KEY = " google.analytics"
21- val INFLUX_AUTH_CONFIG_KEY = " influx.uri"
22- val MONGO_AUTH_CONFIG_KEY = " mongo.uri"
16+ val QUEUE_NAME_DEFAULT_VALUE = " "
2317}
2418
2519class EventSinkService {
2620 val messageService : MessageService = DI .injector.getInstance(classOf [MessageService ])
2721 val userService : UserService = DI .injector.getInstance(classOf [UserService ])
2822 val appConfig : AppConfigurationService = DI .injector.getInstance(classOf [AppConfigurationService ])
2923
30- // UNUSED: Fetch directly from config on demand
31- def getGoogleAnalytics (): String = Play .configuration.getString(EventSinkService .GA_CONFIG_KEY ).getOrElse(" " )
32- def getAmplitudeApiKey (): String = Play .configuration.getString(EventSinkService .AMPLITUDE_CONFIG_KEY ).getOrElse(" " )
33- def getMongoAuth (): String = Play .configuration.getString(EventSinkService .AMPLITUDE_CONFIG_KEY ).getOrElse(" " )
34- def getInfluxAuth (): String = Play .configuration.getString(EventSinkService .INFLUX_AUTH_CONFIG_KEY ).getOrElse(" " )
35-
3624 /** Event Sink exchange name in RabbitMQ */
3725 val exchangeName = Play .configuration.getString(EventSinkService .EXCHANGE_NAME_CONFIG_KEY )
3826 .getOrElse(EventSinkService .EXCHANGE_NAME_DEFAULT_VALUE )
@@ -41,20 +29,22 @@ class EventSinkService {
4129 val queueName = Play .configuration.getString(EventSinkService .QUEUE_NAME_CONFIG_KEY )
4230 .getOrElse(EventSinkService .QUEUE_NAME_DEFAULT_VALUE )
4331
44- def logEvent (category : String , metadata : JsValue ) = {
45- Logger .info(" eventsink.exchangename=" + exchangeName)
46- Logger .info(" eventsink.queueName=" + queueName)
47-
48- Logger .info(" Submitting message to event sink exchange: " + Json .stringify(metadata))
49-
50- // val message = EventSinkMessage(Instant.now().getEpochSecond, category, metadata)
51- messageService.submit(exchangeName, queueName, metadata, " fanout" )
32+ def logEvent (message : JsValue ) = {
33+ // Inject timestamp before logging the event
34+ val event = message.as[JsObject ] + (" created" -> Json .toJson(java.util.Date .from(Instant .now())))
35+ Logger .info(" Submitting message to event sink exchange: " + Json .stringify(event))
36+ try {
37+ messageService.submit(exchangeName, queueName, event, " fanout" )
38+ } catch {
39+ case e : Throwable => { Logger .error(" Failed to submit event sink message" , e) }
40+ }
5241 }
5342
5443 /** Log an event when user signs up */
5544 def logUserSignupEvent (user : User ) = {
56- Logger .info(" New user signed up: " + user.id.stringify)
57- logEvent(" user_activity" , Json .obj(
45+ Logger .debug(" New user signed up: " + user.id.stringify)
46+ logEvent(Json .obj(
47+ " category" -> " user_activity" ,
5848 " type" -> " signup" ,
5949 " user_id" -> user.id,
6050 " user_name" -> user.fullName
@@ -63,8 +53,9 @@ class EventSinkService {
6353
6454 /** Log an event when user logs in */
6555 def logUserLoginEvent (user : User ) = {
66- Logger .info(" User logged in: " + user.id.stringify)
67- logEvent(" user_activity" , Json .obj(
56+ Logger .debug(" User logged in: " + user.id.stringify)
57+ logEvent(Json .obj(
58+ " category" -> " user_activity" ,
6859 " type" -> " login" ,
6960 " user_id" -> user.id,
7061 " user_name" -> user.fullName
@@ -73,193 +64,203 @@ class EventSinkService {
7364
7465 /** Log an event when user views a dataset */
7566 def logDatasetViewEvent (dataset : Dataset , viewer : Option [User ]) = {
76- Logger .info(" User viewed a dataset: " + dataset.id.stringify)
77- logEvent(" view_resource" , Json .obj(
67+ Logger .debug(" User viewed a dataset: " + dataset.id.stringify)
68+ logEvent(Json .obj(
69+ " category" -> " view_resource" ,
7870 " type" -> " dataset" ,
7971 " resource_id" -> dataset.id,
8072 " resource_name" -> dataset.name,
8173 " author_id" -> dataset.author.id,
8274 " author_name" -> dataset.author.fullName,
83- " viewer_id " -> viewer.get .id,
84- " viewer_name " -> viewer.get .getMiniUser.fullName
75+ " user_id " -> viewer.getOrElse( User .anonymous) .id,
76+ " user_name " -> viewer.getOrElse( User .anonymous) .getMiniUser.fullName
8577 ))
8678 }
8779
8880 /** Log an event when user views a file */
8981 def logFileViewEvent (file : File , viewer : Option [User ]) = {
90- Logger .info(" User viewed a file: " + file.id.stringify)
91- logEvent(" view_resource" , Json .obj(
82+ Logger .debug(" User viewed a file: " + file.id.stringify)
83+ logEvent(Json .obj(
84+ " category" -> " view_resource" ,
9285 " type" -> " file" ,
9386 " resource_id" -> file.id,
9487 " resource_name" -> file.filename,
9588 " author_id" -> file.author.id,
9689 " author_name" -> file.author.fullName,
97- " viewer_id " -> viewer.get .id,
98- " viewer_name " -> viewer.get .getMiniUser.fullName
90+ " user_id " -> viewer.getOrElse( User .anonymous) .id,
91+ " user_name " -> viewer.getOrElse( User .anonymous) .getMiniUser.fullName
9992 ))
10093 }
10194
10295 /** Log an event when user views a collection */
10396 def logCollectionViewEvent (collection : Collection , viewer : Option [User ]) = {
104- Logger .info(" User viewed a collection: " + collection.id.stringify)
105- logEvent(" view_resource" , Json .obj(
97+ Logger .debug(" User viewed a collection: " + collection.id.stringify)
98+ logEvent(Json .obj(
99+ " category" -> " view_resource" ,
106100 " type" -> " collection" ,
107101 " resource_id" -> collection.id,
108102 " resource_name" -> collection.name,
109103 " author_id" -> collection.author.id,
110104 " author_name" -> collection.author.fullName,
111- " viewer_id " -> viewer.get .id,
112- " viewer_name " -> viewer.get .getMiniUser.fullName
105+ " user_id " -> viewer.getOrElse( User .anonymous) .id,
106+ " user_name " -> viewer.getOrElse( User .anonymous) .getMiniUser.fullName
113107 ))
114108 }
115109
116110 /** Log an event when user views a space */
117111 def logSpaceViewEvent (space : ProjectSpace , viewer : Option [User ]) = {
118- Logger .info (" User viewed a space: " + space.id.stringify)
112+ Logger .debug (" User viewed a space: " + space.id.stringify)
119113 (viewer, userService.get(space.creator)) match {
120114 case (Some (v), Some (author)) => {
121- logEvent(" view_resource" , Json .obj(
115+ logEvent(Json .obj(
116+ " category" -> " view_resource" ,
122117 " type" -> " space" ,
123118 " resource_id" -> space.id,
124119 " resource_name" -> space.name,
125120 " author_id" -> space.creator.stringify,
126121 " author_name" -> author.fullName,
127- " viewer_id " -> v.id,
128- " viewer_name " -> v.getMiniUser.fullName
122+ " user_id " -> v.id,
123+ " user_name " -> v.getMiniUser.fullName
129124 ))
130125 }
131126 case (None , Some (author)) => {
132- logEvent(" view_resource" , Json .obj(
127+ logEvent(Json .obj(
128+ " category" -> " view_resource" ,
133129 " type" -> " space" ,
134130 " resource_id" -> space.id,
135131 " resource_name" -> space.name,
136132 " author_id" -> author.id,
137133 " author_name" -> author.fullName,
138- " viewer_id " -> " " ,
139- " viewer_name " -> " Anonymous "
134+ " user_id " -> User .anonymous.id ,
135+ " user_name " -> User .anonymous.fullName
140136 ))
141137 }
142138 case (Some (v), None ) => {
143139 // TODO: Is this a real case? Is this needed?
144- logEvent(" view_resource" , Json .obj(
140+ logEvent(Json .obj(
141+ " category" -> " view_resource" ,
145142 " type" -> " space" ,
146143 " resource_id" -> space.id,
147144 " resource_name" -> space.name,
148145 " author_id" -> space.creator.stringify,
149146 " author_name" -> " " ,
150- " viewer_id " -> v.id,
151- " viewer_name " -> v.getMiniUser.fullName
147+ " user_id " -> v.id,
148+ " user_name " -> v.getMiniUser.fullName
152149 ))
153150 }
154151 case (None , None ) => {
155152 // TODO: Is this a real case? Is this needed?
156- logEvent(" view_resource" , Json .obj(
153+ logEvent(Json .obj(
154+ " category" -> " view_resource" ,
157155 " type" -> " space" ,
158156 " resource_id" -> space.id,
159157 " resource_name" -> space.name,
160158 " author_id" -> space.creator.stringify,
161159 " author_name" -> " " ,
162- " viewer_id " -> " " ,
163- " viewer_name " -> " Anonymous "
160+ " user_id " -> User .anonymous.id ,
161+ " user_name " -> User .anonymous.fullName
164162 ))
165163 }
166164 }
167165 }
168166
169167 def logSubmitFileToExtractorEvent (file : File , extractorName : String , submitter : Option [User ]) = {
170- logEvent(" extraction" , Json .obj(
168+ logEvent(Json .obj(
169+ " category" -> " extraction" ,
171170 " type" -> " file" ,
172171 " extractor_name" -> extractorName,
173172 " resource_id" -> file.id,
174173 " resource_name" -> file.filename,
175174 " author_id" -> file.author.id,
176175 " author_name" -> file.author.fullName,
177- " submitter_id " -> submitter.get .id,
178- " submitter_name " -> submitter.get .getMiniUser.fullName
176+ " user_id " -> submitter.getOrElse( User .anonymous) .id,
177+ " user_name " -> submitter.getOrElse( User .anonymous) .getMiniUser.fullName
179178 ))
180179 }
181180
182181 def logSubmitDatasetToExtractorEvent (dataset : Dataset , extractorName : String , submitter : Option [User ]) = {
183- logEvent(" extraction" , Json .obj(
182+ logEvent(Json .obj(
183+ " category" -> " extraction" ,
184184 " type" -> " dataset" ,
185185 " extractor_name" -> extractorName,
186186 " resource_id" -> dataset.id,
187187 " resource_name" -> dataset.name,
188188 " author_id" -> dataset.author.id,
189189 " author_name" -> dataset.author.fullName,
190- " submitter_id " -> submitter.get .id,
191- " submitter_name " -> submitter.get .getMiniUser.fullName
190+ " user_id " -> submitter.getOrElse( User .anonymous) .id,
191+ " user_name " -> submitter.getOrElse( User .anonymous) .getMiniUser.fullName
192192 ))
193193 }
194194
195195 def logSubmitSelectionToExtractorEvent (dataset : Dataset , extractorName : String , submitter : Option [User ]) = {
196196 // TODO: Is this a real case? Is this needed?
197- logEvent(" extraction" , Json .obj(
197+ logEvent(Json .obj(
198+ " category" -> " extraction" ,
198199 " type" -> " selection" ,
199200 " extractor_name" -> extractorName,
200201 " resource_id" -> dataset.id,
201202 " resource_name" -> dataset.name,
202203 " author_id" -> dataset.author.id,
203204 " author_name" -> dataset.author.fullName,
204- " submitter_id " -> submitter.get .id,
205- " submitter_name " -> submitter.get .getMiniUser.fullName
205+ " user_id " -> submitter.getOrElse( User .anonymous) .id,
206+ " user_name " -> submitter.getOrElse( User .anonymous) .getMiniUser.fullName
206207 ))
207208 }
208209
209210 def logFileUploadEvent (file : File , dataset : Option [Dataset ], uploader : Option [User ]) = {
210211 dataset match {
211212 case Some (d) => {
212- logEvent(" upload" , Json .obj(
213+ logEvent(Json .obj(
214+ " category" -> " upload" ,
213215 " dataset_id" -> d.id,
214216 " dataset_name" -> d.name,
215- " dataset_author_name " -> d.author.fullName,
216- " dataset_author_id " -> d.author.id,
217- " uploader_id " -> uploader.get .id,
218- " uploader_name " -> uploader.get .getMiniUser.fullName,
219- " filename " -> file.filename,
220- " length " -> file.length
217+ " author_name " -> d.author.fullName,
218+ " author_id " -> d.author.id,
219+ " user_id " -> uploader.getOrElse( User .anonymous) .id,
220+ " user_name " -> uploader.getOrElse( User .anonymous) .getMiniUser.fullName,
221+ " resource_name " -> file.filename,
222+ " size " -> file.length
221223 ))
222224 }
223225 case None => {
224- logEvent(" upload" , Json .obj(
225- " uploader_id" -> uploader.get.id,
226- " uploader_name" -> uploader.get.getMiniUser.fullName,
227- " filename" -> file.filename,
228- " length" -> file.length
226+ logEvent(Json .obj(
227+ " category" -> " upload" ,
228+ " user_id" -> uploader.getOrElse(User .anonymous).id,
229+ " user_name" -> uploader.getOrElse(User .anonymous).getMiniUser.fullName,
230+ " resource_name" -> file.filename,
231+ " size" -> file.length
229232 ))
230233 }
231234 }
232235 }
233236
234- def logFileDownloadEvent (file : File , /* dataset: Dataset,*/ downloader : Option [User ]) = {
235- logEvent(" download" , Json .obj(
236- /* "dataset_id" -> dataset.id,
237- "dataset_name" -> dataset.name,
238- "dataset_author_name" -> dataset.author.fullName,
239- "dataset_author_id" -> dataset.author.id,*/
237+ def logFileDownloadEvent (file : File , downloader : Option [User ]) = {
238+ logEvent(Json .obj(
239+ " category" -> " download" ,
240240 " type" -> " file" ,
241- " uploader_id" -> file.author.id,
242- " uploader_name" -> file.author.fullName,
243- " downloader_id" -> downloader.get.id,
244- " downloader_name" -> downloader.get.getMiniUser.fullName,
245- " filename" -> file.filename,
246- " length" -> file.length
241+ " resource_id" -> file.id,
242+ " resource_name" -> file.filename,
243+ " author_id" -> file.author.id,
244+ " author_name" -> file.author.fullName,
245+ " user_id" -> downloader.getOrElse(User .anonymous).id,
246+ " user_name" -> downloader.getOrElse(User .anonymous).getMiniUser.fullName,
247+ " size" -> file.length
247248 ))
248249 }
249250
250251 def logDatasetDownloadEvent (dataset : Dataset , downloader : Option [User ]) = {
251- logEvent(" download" , Json .obj(
252+ logEvent(Json .obj(
253+ " category" -> " download" ,
252254 " type" -> " dataset" ,
253- " dataset_id" -> dataset.id,
254- " dataset_name" -> dataset.name,
255- " dataset_author_name" -> dataset.author.fullName,
256- " dataset_author_id" -> dataset.author.id,
257- " downloader_id" -> downloader.get.id,
258- " downloader_name" -> downloader.get.getMiniUser.fullName,
259- " files_length" -> dataset.files.length,
260- " folder_length" -> dataset.folders.length
255+ " resource_id" -> dataset.id,
256+ " resource_name" -> dataset.name,
257+ " author_name" -> dataset.author.fullName,
258+ " author_id" -> dataset.author.id,
259+ " user_id" -> downloader.getOrElse(User .anonymous).id,
260+ " user_name" -> downloader.getOrElse(User .anonymous).getMiniUser.fullName,
261+ " size" -> (dataset.files.length + dataset.folders.length)
261262 ))
262263 }
263264}
264265
265- // case class EventSinkMessage(created: Long, category: String, metadata: JsValue)
266+ // case class EventSinkMessage(created: Long, category: String, metadata: JsValue)
0 commit comments