I am not able to view detailed URL Patterns in Firebase Performance Monitoring even after the API having >30k samples in the past few months.
It just shows the root API domain like this:
api.myAppName.in/**
instead of something like
api.myAppName.in/app/v3/user/*/profile/
The console just shows a label "uncategorized" on the API with this message if you hover it
Detailed URL patterns from api.myAppName.in will appear as we collect
a larger number of samples. Allow for up to 24 hours after collection.
But as mentioned earlier, it's been a few months and more than 30k samples.
I'm using retrofit, if that helps.
I have contacted Firebase support and their answer was:
...
As mentioned here, while Performance Monitoring reports most network requests for your app, some might not be reported. Hence, it is recommended to add monitoring for specific requests in your app.
In order for a request to be reported to the Performance Monitoring console, any trace that is start using the trace.start() must also be
stopped by calling the trace.stop() method. Traces that are never
stopped, are never reported.
Wildcarding is only based on path segment and not query strings. Query strings are ignored for purpose of aggregation in the dashboard.
For a domain a.b.c, it's path a.b.c/d/ should have been requested from several dozen unique devices in order for the path a.b.c/d/ to
appear separately in the dashboard, in the time frame of your selected
filter.
Known Issues with Performance Monitoring.
For the 4th point that I mentioned above, there is also one thing to
keep in mind. Let's say N is the definite numerical value representing
the "several dozen" threshold that I mentioned earlier. And, we are
monitoring the following 3 different path segments:
1. www.something.com/a/
2. www.something.com/b/
3. www.something.com/c/
Within a given time-frame, if all 3 of the paths above received N-1
hits. That is, not meeting the threshold requirement. While the number
of hits might appear to be almost 3 times of N when seen against
www.something.com collectively(as that is what the dashboard will
show), the individual hits for each path did not meet the threshold in
the given time frame and hence, only the aggregated number statistics
are shown.
...
The code that I used to intercept the retrofit request and monitor the request time is: (I removed the query params data for security)
val builder = OkHttpClient.Builder()
.addInterceptor(FirebasePerformanceInterceptor(FirebasePerformance.getInstance()))
.build()
// Adapted from: http://qaru.site/questions/15007824/how-can-i-get-the-url-and-method-of-retrofit-request-on-onsubscribe-of-rxjava-2-custom-operator
class FirebasePerformanceInterceptor(private val performanceInstance: FirebasePerformance) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
//Get request values
val url = request.url().url()
val urlPath = getUrlWithoutQuery(url)
val requestPayloadSize = request.body()?.contentLength() ?: 0L
val httpMethod = request.method()
//Initialize http trace
val trace = performanceInstance.newHttpMetric(urlPath, httpMethod)
trace.setRequestPayloadSize(requestPayloadSize)
trace.start()
//Proceed
val response = chain.proceed(chain.request())
//Get response values
val responseCode = response.code()
//Add response values to trace and close it
trace.setHttpResponseCode(responseCode)
trace.stop()
return response
}
}
private fun getUrlWithoutQuery(url: URL): URL {
val uri = URI(url.protocol, url.host, url.path, null)
return uri.toURL()
}
To test if it's logging correctedly: Follow this Debugging tutorial:
You will see something like:
10-24 19:48:21.162 23037 24411 I FirebasePerformance: Logging NetworkRequestMetric - https://your-api-domain.com/cart 0b 147809ms,
Firebase Performance Monitoring now supports creating custom URL patterns that allow you to target more specific URLs. From the docs:
You can create custom URL patterns to monitor specific URL patterns that Firebase isn't capturing with its derived automatic URL pattern matching. For example, you can use a custom URL pattern to troubleshoot a specific URL or to monitor a specific set of URLs over time.
So if you did want to capture something like api.myAppName.in/app/v3/user/*/profile/**, you would now be able to 😃
See the docs for more details.
Related
I've been playing with Firebase transactions with Android for some time and that thought crossed my mind.
When you run a transaction like this one the variable data gets assigned instantly using a synchronous call to transaction.get():
val db = Firebase.firestore
db.runTransaction { transaction ->
val ref = db.collection("example").document("exampledoc")
val ref2 = db.collection("example").document("exampledoc2")
val data = transaction.get(ref)
val data2 = transaction.get(ref2)
}
How can it get the value instantly? Does it request a copy of the entire database or of a portion of it before running the transaction and then instantly returns the values when they are requested while running it? If it requests a copy of a portion of the database, how does it know what portion to get before the requests are even made?
I looked for this everywhere and couldn't find anyone explaining how it works.
It's not really instant, but it is synchronous, and nothing is preloaded. Each call to get() inside a transaction requests that specific document to be fetched and returned on demand so you can work with its contents inside the transaction handler. If you have a slow network connection, you will more clearly observe the delay. Try adding some timing code - you will see that it is not single-digit milliseconds that you would observe from something that is truly instant from being in memory.
While you might expect the method to return a Task like other methods that read and write the database, that is just not the case in a transaction. The API assumes that you need the data immediately and hides the implementation detail of doing the request so that you don't have to worry about dealing with so many tasks.
You might be helped further by reading the source code.
I'm trying to find a proper RxJava 2 operator for my specific need.
I have 2 server requests.
val singletonOne = repository.loadData(requestOne) // Returns Single
val singletonTwo = repository.loadData(requestTwo) // Returns Single
val combinedSingle = Single.zip(singletonOne, singletonTwo) { responseOne: MyResponse, responseTwo: MyResponse
CombinedResponse(responseOne, responseTwo)
}
Then I'm subscribing to my combined Single and can handle 2 responses extremely racting them from CombinedResponse.
RxUtil(schedulers).observe(combinedSingle).subscribe(combinedResponse -> {
view.updateSmthOne(combinedResponse.firstResponse.data)
view.updateSmthTwo(combinedResponse.secondResponse.data)
}, {/*Handle error*/})
Everything is fine wit this approach. But now I need to add one small change.
I need to get one field from the 1st response use it to update one field of the 2d request and as a result I should get the same CombinedSingle.
I went through RxJava 2 docs and could not find proper operator which could solve my problem.
Note
Yes, I can subscribe to the 1st single, get it's result and on onSuccess(...) make 2nd server request with updated field. That works, but I'm looking for better solutions with RxJava.
Thanks in advance.
I have a library that produces values from reading data from different sources and offers them to a flow from which the user of the library can collect
to do so I use this code
private val dataChannel = BroadcastChannel<Reading>(10)
val dataFlow get() = dataChannel.asFlow()
//this may be triggered by a 5 different threads
fun newReading(type:String, value:Float, time:Date, level:Int) {
dataChannel.offer(Reading(type,value,time,Level.fromValue(level))
}
the user can do something like this to get the data
lastJob?.cancel()
lastJob = launch {
lib.dataFlow.flowOn(Dispatchers.Default).collect { reading ->
val result = processReading(reading)
withContext(Dispatchers.IO) {
Toast.makeText(application.applicationContext, result,Toast.LENGTH_LONG).show()
}
}
}
this works as expected, when the user launches the job my library may keep on sending data and the user of the library will keep on receiving them
however if the user cancels lastJob and then after a few seconds launches it again they will loose the data received by my library between those two times
is there away to make dataChannel store all the readings received through offer in that timespan (up to the buffer level of 10 set in its constructor) and when the dataFlow becomes active again to emit those values at the same time?
You can try out some of the following options:
BroadcastChannel<String>(Channel.BUFFERED).asFlow(), this will initialise your channel with the capacity of the DEFAULT_BUFFER_PROPERTY_NAME, which is 64 by default, but in can be overridden on JVM.
You can create a Channel(Channel.UNLIMITED) then consumeAsFlow(), but yeah, having an unlimited buffer is generally a bad practice
Create a Channel(Channel.RENDEZVOUS), and use send() instead of offer(). Rendezvous channels are basically channels with 0 capacity and whenever your values are not consumed/collected, it will suspend the send() call.
I am new to kotlin / android development, I have been following the google / android docs with using Volley to send requests....Still have not go to handling JSON yet.
When I put the example GET in a function in kotlin using android development studio, I am getting the error message from ErrorListener
val textView = findViewById<TextView>(R.id.textReturn)
val url = "http://wwww.google.com"
val queue = Volley.newRequestQueue(this)
val stringRequest =
StringRequest(Request.Method.GET, url, Response.Listener<String> { response ->
var strRes = response.toString()
textView.text = strRes
}, Response.ErrorListener {textView.text = "That didn't work!"})
queue.add(stringRequest)
}
I am unsure on how to debug and come from a very javascript / php background where console.log() or print() will post to the console, kotlin does not seem to have such a simple function for this.
Not exactly certain about Volley, but eg. with Retrofit, the error listener will only be triggered on hard errors - but common HTTP status codes, besides 200 OK, end up in the response listener. Retrofit with GsonConverter and ORM is the most common combo for converting JSON to POJO (one can define a whole API as a web-service and it converts the JSON automatically). However, when still learning Java/Kotlin, it might not hurt to use Volley and not use ORM auto-mapping (because all the nuts & bolts are being abstracted away). PHP background is useful for whatever API interaction.
And concerning the actual question, in Kotlin one can log with: println and Android Studio can be used for the actual debugging (it provides similar functionality alike F12 tools and xdebug provide). Using logging to debug is only partially applicable, eg. when wanting to let the console tell the story of what happened - otherwise, just use break-points, watches or evaluate expressions.
If I have a Spring app with a mail server inbound channel, what is the best way to process every file in every email (I poll approx. every 1 min, and fetch 1 email with multiple attachments).
Although I can apply multithreading at the receiving channel (SimpleAsyncTaskExecutor or ThreadPoolTaskExecutor) this doesn't help much because if I have 10 files attached in an email, their processing is pretty much bound to one thread.
I've been keeping this pretty synchronous until now, because I wanted to aggregate some data for every email, and send a response after all files have been processed. I believe this could also be done in a better way.
In general how can I asynchronously process every file in every email, and then again asynchronously build an email reply?
Looks like you are asking for java.util.concurrent.Future. This is a java core concept to block until a (method) result is calculated. (see JavaDoc for an example)
The Spring #Async support the Future concept too.
So the only think you need to do is having a method that uses #Async takes one attachment of the Mail as argument and returns what ever is calculated in a future.
The you need to invoke all this methods for all attachments (asynchronous) and store the immediately returned future in a list. After all methods are invoked. You try to get the feature results in an new loop. After this loop is finish all attachments are proceed asynchronous.
processOneMail(List<Attachement> attachments) {
List<Future<AttachmentResult>> futures = new ArrayList...
for(Attachment attachment : attachments) {
futures.add(processOneAttachment(attachment)); //async
}
List<AttachmentResult> attachmentResults = new ArrayList...
for(Future<AttachmentResult>> future : futures) {
attachmentResults.add(future.get()); //eventually blocks
}
//now all attachments are calculated and stored in the list.
...
}
#Async
Future<AttachmentResult> processOneAttachment(Attachment attachment) {
...
}
See also: http://blog.espenberntsen.net/2010/03/08/spring-asynchronous-support/