matomo-org / matomo-sdk-android

SDK for Android to measure your apps with Matomo. Works on Android phones, tablets, Fire TV sticks, and more!
BSD 3-Clause "New" or "Revised" License
393 stars 164 forks source link

Set custom dimension/variable by itself #224

Closed ThomasGoehringer closed 5 years ago

ThomasGoehringer commented 6 years ago

Could you add the possibility to track custom dimensions or custom variables without having to track a screen or an event? Currently my visitor log is cluttered with unnecessary actions because of the custom variables I'm tracking. A solution like the user ID would be great.

d4rken commented 6 years ago

AFAIK this is a limitation of the server API, not this library.

ThomasGoehringer commented 6 years ago

I don't know how it's done in the original Javascript library and in the iOS SDK, but there are no unnecessary actions like the first three in the screenshot below.

matomo

d4rken commented 6 years ago

It doesn't need three, but at least one..

@mattab can custom dimensions be tracked without an action or view event?

mattab commented 6 years ago

@d4rken the normal flow is to call the setCustomDimension before tracking an action. So it's important to know, and set, the value before the standard "track pageview" is called. The correct way looks like this:

setCustomDimension
trackXYZ

If you do the below, then the custom dimension is set too late and Matomo won't know about the values:

trackXYZ
setCustomDimension

And if you do the following below, only the first call to setCustomDimension actually will work:

setCustomDimension
trackXYZ
setCustomDimension

In terms of the SDK, it's important that "setCustomDimension" or other "setXYZ" methods, don't send a HTTPs request. only "trackXYZ" methods would send a request. Hope it helps?

d4rken commented 6 years ago

That's how I understood it, thanks for the confirmation.

The SDK's TrackHelper offers this helper function to track dimensions with "pageviews".

https://github.com/matomo-org/piwik-sdk-android/blob/a3c0a4189f58ec88199bf54b74f5bf84a722edba/piwik-sdk/src/main/java/org/piwik/sdk/extra/TrackHelper.java#L142

The "Event" tracking doesn't have such a helper function at the moment but if Matomo supports dimensions with events, then I could add another helper function for that too.

@ThomasGoehringer Does that answer your questions? This currently does not look like something that can be changed. Your multiple items shouldn't be necessary if you set all 3 dimensions on the same TrackMe.

mattab commented 6 years ago

Yes, dimensions are supported for events. @ThomasGoehringer Maybe your issue comes from using the Helper method that does dimension+track, thinking it would only set the dimension?

ThomasGoehringer commented 6 years ago

I'm still using the old custom variables like

CustomVariables customVariables = new CustomVariables();
customVariables.put(index, name, value);
getTracker().track(customVariables.toVisitVariables());

I somehow can't get custom dimensions to work.

The way it is implemented at the moment I always have at least one unnecessary screen being tracked. I think if there was a setCustomVariable/setCustomDimension like @mattab said, this would be a better solution.

d4rken commented 6 years ago

The way it is implemented at the moment I always have at least one unnecessary screen being tracked.

See above, you NEED to track a screen or event, you can't track dimensions on their own.

I think if there was a setCustomVariable/setCustomDimension like @mattab said, this would be a better solution.

v1.0 of the Tracker had such a method but I removed it as we can't control when they are transmitted, especially with lots of tracking going on. The current way makes it clear that these dimensions can only be tracked if part of an event or screenview.

If your dimensions are not all available at the same time and you want to minimize events, then you'd need to store them sepperately until all of your dimensions are available and then send them with the next tracked thing.

mattab commented 6 years ago

See above, you NEED to track a screen or event, you can't track dimensions on their own.

It makes sense that "Action scope custom dimensions" are set on the screen or even itself.

But how do you manage the "Visit scope custom dimensions"?

If your dimensions are not all available at the same time and you want to minimize events, then you'd need to store them sepperately until all of your dimensions are available and then send them with the next tracked thing.

For "Visit scope" dimensions attached to visits, is there/would it be possible to have a "setDimension" method that people can call to set the dimension value on the visit? (this would be consistent with other SDKs that offer this "set" method as well as ability to set the custom dimensions in the screen or event)

ThomasGoehringer commented 6 years ago

For "Visit scope" dimensions attached to visits, is there/would it be possible to have a "setDimension" method that people can call to set the dimension value on the visit? (this would be consistent with other SDKs that offer this "set" method as well as ability to set the custom dimensions in the screen or event)

That's exactly what I meant and need.

d4rken commented 6 years ago

I think we need to distinguish here between custom dimensions (new thing) and custom variables (deprecated).

From the SDK's point of view there is no scope difference for dimensions as this is handled by the server and distinguished by IDs. The SDK can't know which is which and it's up to the dev to specify their correct IDs.

For the custom variables, there is a helper method CustomVariables.injectVisitVariables(TrackMe).

As an example, due to having used both, i have a helper method that looks like this:

public void trackIt(TrackMe trackMe) {
        CustomVariables merged = new CustomVariables();
        CustomVariables customVar;
// This queue gets filled asynchronously by other events.
        while ((customVar = customVariableQueue.poll()) != null) {
            merged.putAll(customVar);
        }

        Pair<Integer, String> customDimension;
// This queue also gets filled asynchronously by other events
        while ((customDimension = customDimensionQueue.poll()) != null) {
            CustomDimension.setDimension(trackMe, customDimension.first, customDimension.second);
        }
// When ever something is tracked, make sure to send the visit variables a long.
        tracker.track(merged.injectVisitVariables(trackMe));
    }

For "Visit scope" dimensions attached to visits, is there/would it be possible to have a "setDimension" method that people can call to set the dimension value on the visit? (this would be consistent with other SDKs that offer this "set" method as well as ability to set the custom dimensions in the screen or event)

A setDimension method where? On TrackHelper for some type of event? Sure!

On Tracker? I'll need some more arguments for that. A setDimension here would be something like a queue that gets filled, but with no guarantee that another event is coming that empties that queue. So to make sure that the server gets these custom dimensions we need to ensure that we track something. The SDK can't do that, the developer needs to make sure that something is tracked otherwise the app is closed and dimensions were never transmitted. But if the developer needs to ensure that this happens the developer can just set them on TrackMe and transmit it when they think it's ready.

I'd like to keep Tracker as clean and to the basics as possible, i.e. non opinionated. Maybe another helper class would be the solution here? Hm... Maybe a callback in the Tracker like onPreTrack(TrackMe) where one could hook into to check whether any additional changes are necessary like outstanding custom dimensions that are still to be send...

Or am I misunderstanding something here?

d4rken commented 6 years ago

I guess we already have:

CustomDimensions.setDimension(tracker.getDefaultTrackMe(), 1, "my value")

Though this would cost more traffic for the user as the dimensions will be send with EVERY request. Theoretically we could add a generalpurpose method to the Tracker like addOneTimeValue(String,Object) that only gets send once with the next request.

AFAIK the iOS SDK just sends the variables with every tracking event:

https://github.com/matomo-org/matomo-sdk-ios/blob/f390627863b2441374211051567dbc07e1f4e06a/MatomoTracker/MatomoTracker.swift#L328

AFAIK even the visit variables only need to be send once unless they change.

I guess I want to minimize the amount of traffic that is generated for the user and thus move most of the "lifting" to the developer, because the SDK can't do this efficiently. I'm happy to provide more helpers to make it easier, but don't want to clutter or even enforce a behaviour that prevents other devs from optimising this in their app which is why the Tracker should stay so "basic".

ThomasGoehringer commented 6 years ago

Sorry for the late response. Maybe CustomDimensions will already do the trick. However, as I already said CustomDimensions.setDimension(...) doesn't track anything when I use it. Is there anything special I need to pay attention to?

Theoretically we could add a generalpurpose method to the Tracker like addOneTimeValue(String,Object) that only gets send once with the next request.

This sounds pretty good for me, too.

d4rken commented 6 years ago

No worries I'm a bit distracted by other projects too.

I'm thinking about making this change in v4 (see #220).

Hope to get some hours for that next week.

Theoretically we could add a generalpurpose method to the Tracker like addOneTimeValue(String,Object) that only gets send once with the next request.

The caveat here is that it offers hidden traps for devs as not all parameters are compatible with all tracking requests.

Imagine that you call addOneTimeValue(parameterA) but parameterA is only valid for trackEvent type actions and not trackPageView calls. Everything is async, so there is no real order to which type would then consume our "one time value" and it would possibly get lost...

I think I would favor a general callback that executes shortly before tracking, and then we could write a helper class that can be attached to the tracker and internally uses the callback. This allows any such helper class to check the type of event and then modify the data as necessary, e.g. include dimensions. Would allow more extensions for other use-cases too.