[MM-13955] Add telemetry for Android (#2719)
* add markers for Android profiling * update per comments * Remove beta build flag * update per comment, latest change on telemetry server and others * rebase and fix merge conflicts, and update per comments * update commit hash of mattermost-redux
This commit is contained in:
parent
5ce9208d32
commit
e086d66ec2
28 changed files with 554 additions and 29 deletions
|
|
@ -44,6 +44,15 @@ import com.wix.reactnativenotifications.core.AppLaunchHelper;
|
|||
import com.wix.reactnativenotifications.core.AppLifecycleFacade;
|
||||
import com.wix.reactnativenotifications.core.JsIOHelper;
|
||||
|
||||
import com.facebook.react.ReactPackage;
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.bridge.ReactMarker;
|
||||
import com.facebook.react.bridge.ReactMarkerConstants;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.facebook.react.modules.core.DeviceEventManagerModule;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
public class MainApplication extends NavigationApplication implements INotificationsApplication, INotificationsDrawerApplication {
|
||||
|
|
@ -51,6 +60,14 @@ public class MainApplication extends NavigationApplication implements INotificat
|
|||
public Boolean sharedExtensionIsOpened = false;
|
||||
public Boolean replyFromPushNotification = false;
|
||||
|
||||
public long APP_START_TIME;
|
||||
|
||||
public long RELOAD;
|
||||
public long CONTENT_APPEARED;
|
||||
|
||||
public long PROCESS_PACKAGES_START;
|
||||
public long PROCESS_PACKAGES_END;
|
||||
|
||||
@Override
|
||||
public boolean isDebug() {
|
||||
return BuildConfig.DEBUG;
|
||||
|
|
@ -109,6 +126,9 @@ public class MainApplication extends NavigationApplication implements INotificat
|
|||
setActivityCallbacks(notificationsLifecycleFacade);
|
||||
|
||||
SoLoader.init(this, /* native exopackage */ false);
|
||||
|
||||
// Uncomment to listen to react markers for build that has telemetry enabled
|
||||
// addReactMarkerListener();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -133,4 +153,36 @@ public class MainApplication extends NavigationApplication implements INotificat
|
|||
public IPushNotificationsDrawer getPushNotificationsDrawer(Context context, AppLaunchHelper defaultAppLaunchHelper) {
|
||||
return new CustomPushNotificationDrawer(context, defaultAppLaunchHelper);
|
||||
}
|
||||
|
||||
private void addReactMarkerListener() {
|
||||
ReactMarker.addListener(new ReactMarker.MarkerListener() {
|
||||
@Override
|
||||
public void logMarker(ReactMarkerConstants name, @Nullable String tag, int instanceKey) {
|
||||
if (name.toString() == ReactMarkerConstants.RELOAD.toString()) {
|
||||
APP_START_TIME = System.currentTimeMillis();
|
||||
RELOAD = System.currentTimeMillis();
|
||||
} else if (name.toString() == ReactMarkerConstants.PROCESS_PACKAGES_START.toString()) {
|
||||
PROCESS_PACKAGES_START = System.currentTimeMillis();
|
||||
} else if (name.toString() == ReactMarkerConstants.PROCESS_PACKAGES_END.toString()) {
|
||||
PROCESS_PACKAGES_END = System.currentTimeMillis();
|
||||
} else if (name.toString() == ReactMarkerConstants.CONTENT_APPEARED.toString()) {
|
||||
CONTENT_APPEARED = System.currentTimeMillis();
|
||||
ReactContext ctx = getReactGateway().getReactContext();
|
||||
|
||||
if (ctx != null) {
|
||||
WritableMap map = Arguments.createMap();
|
||||
|
||||
map.putDouble("appReload", RELOAD);
|
||||
map.putDouble("appContentAppeared", CONTENT_APPEARED);
|
||||
|
||||
map.putDouble("processPackagesStart", PROCESS_PACKAGES_START);
|
||||
map.putDouble("processPackagesEnd", PROCESS_PACKAGES_END);
|
||||
|
||||
ctx.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).
|
||||
emit("nativeMetrics", map);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,6 +34,8 @@ import {
|
|||
} from 'mattermost-redux/selectors/entities/channels';
|
||||
import {getCurrentTeamId, getTeamByName} from 'mattermost-redux/selectors/entities/teams';
|
||||
|
||||
import telemetry from 'app/telemetry';
|
||||
|
||||
import {
|
||||
getChannelByName,
|
||||
getDirectChannelName,
|
||||
|
|
@ -536,6 +538,12 @@ export function leaveChannel(channel, reset = false) {
|
|||
}
|
||||
|
||||
export function setChannelLoading(loading = true) {
|
||||
if (loading) {
|
||||
telemetry.start(['channel:loading']);
|
||||
} else {
|
||||
telemetry.end(['channel:loading']);
|
||||
}
|
||||
|
||||
return {
|
||||
type: ViewTypes.SET_CHANNEL_LOADER,
|
||||
loading,
|
||||
|
|
@ -593,6 +601,9 @@ export function increasePostVisibility(channelId, postId) {
|
|||
return true;
|
||||
}
|
||||
|
||||
telemetry.reset();
|
||||
telemetry.start(['posts:loading']);
|
||||
|
||||
dispatch({
|
||||
type: ViewTypes.LOADING_POSTS,
|
||||
data: true,
|
||||
|
|
@ -630,6 +641,9 @@ export function increasePostVisibility(channelId, postId) {
|
|||
}
|
||||
|
||||
dispatch(batchActions(actions));
|
||||
telemetry.end(['posts:loading']);
|
||||
telemetry.save();
|
||||
|
||||
return hasMorePost;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ export default class Post extends PureComponent {
|
|||
renderReplies: PropTypes.bool,
|
||||
isFirstReply: PropTypes.bool,
|
||||
isLastReply: PropTypes.bool,
|
||||
isLastPost: PropTypes.bool,
|
||||
consecutivePost: PropTypes.bool,
|
||||
hasComments: PropTypes.bool,
|
||||
isSearchResult: PropTypes.bool,
|
||||
|
|
@ -237,6 +238,7 @@ export default class Post extends PureComponent {
|
|||
channelIsReadOnly,
|
||||
commentedOnPost,
|
||||
highlight,
|
||||
isLastPost,
|
||||
isLastReply,
|
||||
isSearchResult,
|
||||
onHashtagPress,
|
||||
|
|
@ -336,6 +338,7 @@ export default class Post extends PureComponent {
|
|||
ref={'postBody'}
|
||||
highlight={highlight}
|
||||
channelIsReadOnly={channelIsReadOnly}
|
||||
isLastPost={isLastPost}
|
||||
isSearchResult={isSearchResult}
|
||||
navigator={this.props.navigator}
|
||||
onFailedPostPress={this.handleFailedPostPress}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ import {getMarkdownTextStyles, getMarkdownBlockStyles} from 'app/utils/markdown'
|
|||
import {preventDoubleTap} from 'app/utils/tap';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
|
||||
|
||||
import telemetry from 'app/telemetry';
|
||||
|
||||
let FileAttachmentList;
|
||||
let PostAddChannelMember;
|
||||
let PostBodyAdditionalContent;
|
||||
|
|
@ -43,6 +45,7 @@ export default class PostBody extends PureComponent {
|
|||
highlight: PropTypes.bool,
|
||||
isFailed: PropTypes.bool,
|
||||
isFlagged: PropTypes.bool,
|
||||
isLastPost: PropTypes.bool,
|
||||
isPending: PropTypes.bool,
|
||||
isPostAddChannelMember: PropTypes.bool,
|
||||
isPostEphemeral: PropTypes.bool,
|
||||
|
|
@ -95,6 +98,17 @@ export default class PostBody extends PureComponent {
|
|||
isLongPost: false,
|
||||
};
|
||||
|
||||
logTelemetry = () => {
|
||||
telemetry.end([
|
||||
'channel:switch_initial',
|
||||
'channel:switch_loaded',
|
||||
'posts:list_update',
|
||||
'team:switch',
|
||||
'start:overall',
|
||||
]);
|
||||
telemetry.save();
|
||||
}
|
||||
|
||||
measurePost = (event) => {
|
||||
const {height} = event.nativeEvent.layout;
|
||||
const {showLongPost} = this.props;
|
||||
|
|
@ -104,6 +118,10 @@ export default class PostBody extends PureComponent {
|
|||
isLongPost: true,
|
||||
});
|
||||
}
|
||||
|
||||
if (this.props.isLastPost) {
|
||||
this.logTelemetry();
|
||||
}
|
||||
};
|
||||
|
||||
openLongPost = preventDoubleTap(() => {
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import mattermostManaged from 'app/mattermost_managed';
|
|||
import {makeExtraData} from 'app/utils/list_view';
|
||||
import {changeOpacity} from 'app/utils/theme';
|
||||
import {matchDeepLink} from 'app/utils/url';
|
||||
import telemetry from 'app/telemetry';
|
||||
|
||||
import DateHeader from './date_header';
|
||||
import NewMessagesDivider from './new_messages_divider';
|
||||
|
|
@ -48,6 +49,7 @@ export default class PostList extends PureComponent {
|
|||
highlightPostId: PropTypes.string,
|
||||
initialIndex: PropTypes.number,
|
||||
isSearchResult: PropTypes.bool,
|
||||
lastPostIndex: PropTypes.number.isRequired,
|
||||
lastViewedAt: PropTypes.number, // Used by container // eslint-disable-line no-unused-prop-types
|
||||
navigator: PropTypes.object,
|
||||
onLoadMoreUp: PropTypes.func,
|
||||
|
|
@ -103,6 +105,8 @@ export default class PostList extends PureComponent {
|
|||
this.handleDeepLink(this.props.deepLinkURL);
|
||||
this.props.actions.setDeepLinkURL('');
|
||||
}
|
||||
|
||||
telemetry.start(['posts:list_update']);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
|
|
@ -256,6 +260,7 @@ export default class PostList extends PureComponent {
|
|||
<Post
|
||||
postId={postId}
|
||||
highlight={this.props.highlightPostId === postId}
|
||||
isLastPost={this.props.lastPostIndex === index}
|
||||
{...postProps}
|
||||
/>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ describe('PostList', () => {
|
|||
navigator: {
|
||||
showModal: jest.fn(),
|
||||
},
|
||||
lastPostIndex: -1,
|
||||
postIds: ['post-id-1', 'post-id-2'],
|
||||
serverURL,
|
||||
siteURL: 'https://site-url.fake',
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ import {
|
|||
I18nManager,
|
||||
} from 'react-native';
|
||||
|
||||
import telemetry from 'app/telemetry';
|
||||
|
||||
const MIN_SWIPE_DISTANCE = 3;
|
||||
const DEVICE_WIDTH = parseFloat(Dimensions.get('window').width);
|
||||
const THRESHOLD = DEVICE_WIDTH / 2;
|
||||
|
|
@ -234,6 +236,9 @@ export default class DrawerLayout extends Component {
|
|||
...options,
|
||||
}).start(() => {
|
||||
if (this.props.onDrawerOpen) {
|
||||
telemetry.end(['channel:open_drawer']);
|
||||
telemetry.save();
|
||||
|
||||
this.props.onDrawerOpen();
|
||||
}
|
||||
this._emitStateChanged(IDLE);
|
||||
|
|
@ -250,6 +255,7 @@ export default class DrawerLayout extends Component {
|
|||
...options,
|
||||
}).start(() => {
|
||||
if (this.props.onDrawerClose) {
|
||||
telemetry.end(['channel:close_drawer']);
|
||||
this.props.onDrawerClose();
|
||||
}
|
||||
this._emitStateChanged(IDLE);
|
||||
|
|
|
|||
|
|
@ -12,9 +12,28 @@ import {getCurrentTeamId, getMyTeamsCount} from 'mattermost-redux/selectors/enti
|
|||
import {setChannelDisplayName, setChannelLoading} from 'app/actions/views/channel';
|
||||
import {makeDirectChannel} from 'app/actions/views/more_dms';
|
||||
import {isLandscape, isTablet, getDimensions} from 'app/selectors/device';
|
||||
import telemetry from 'app/telemetry';
|
||||
|
||||
import MainSidebar from './main_sidebar.js';
|
||||
|
||||
export function logChannelSwitch(channelId, currentChannelId) {
|
||||
return (dispatch, getState) => {
|
||||
if (channelId === currentChannelId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const metrics = [];
|
||||
if (getState().entities.posts.postsInChannel[channelId]) {
|
||||
metrics.push('channel:switch_loaded');
|
||||
} else {
|
||||
metrics.push('channel:switch_initial');
|
||||
}
|
||||
|
||||
telemetry.reset();
|
||||
telemetry.start(metrics);
|
||||
};
|
||||
}
|
||||
|
||||
function mapStateToProps(state) {
|
||||
const {currentUserId} = state.entities.users;
|
||||
|
||||
|
|
@ -34,6 +53,7 @@ function mapDispatchToProps(dispatch) {
|
|||
actions: bindActionCreators({
|
||||
getTeams,
|
||||
joinChannel,
|
||||
logChannelSwitch,
|
||||
makeDirectChannel,
|
||||
setChannelDisplayName,
|
||||
setChannelLoading,
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@ import ChannelsList from './channels_list';
|
|||
import DrawerSwiper from './drawer_swipper';
|
||||
import TeamsList from './teams_list';
|
||||
|
||||
import telemetry from 'app/telemetry';
|
||||
|
||||
const DRAWER_INITIAL_OFFSET = 40;
|
||||
const DRAWER_LANDSCAPE_OFFSET = 150;
|
||||
|
||||
|
|
@ -30,6 +32,7 @@ export default class ChannelSidebar extends Component {
|
|||
static propTypes = {
|
||||
actions: PropTypes.shape({
|
||||
getTeams: PropTypes.func.isRequired,
|
||||
logChannelSwitch: PropTypes.func.isRequired,
|
||||
makeDirectChannel: PropTypes.func.isRequired,
|
||||
setChannelDisplayName: PropTypes.func.isRequired,
|
||||
setChannelLoading: PropTypes.func.isRequired,
|
||||
|
|
@ -165,11 +168,14 @@ export default class ChannelSidebar extends Component {
|
|||
};
|
||||
|
||||
selectChannel = (channel, currentChannelId, closeDrawer = true) => {
|
||||
const {setChannelLoading} = this.props.actions;
|
||||
const {logChannelSwitch, setChannelLoading} = this.props.actions;
|
||||
|
||||
logChannelSwitch(channel.id, currentChannelId);
|
||||
|
||||
tracker.channelSwitch = Date.now();
|
||||
|
||||
if (closeDrawer) {
|
||||
telemetry.start(['channel:close_drawer']);
|
||||
this.closeChannelDrawer();
|
||||
setChannelLoading(channel.id !== currentChannelId);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import {ListTypes, ViewTypes} from 'app/constants';
|
|||
import {preventDoubleTap} from 'app/utils/tap';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
|
||||
import tracker from 'app/utils/time_tracker';
|
||||
import telemetry from 'app/telemetry';
|
||||
|
||||
import TeamsListItem from './teams_list_item';
|
||||
|
||||
|
|
@ -55,9 +56,15 @@ export default class TeamsList extends PureComponent {
|
|||
}
|
||||
|
||||
selectTeam = (teamId) => {
|
||||
const {actions, closeChannelDrawer, currentTeamId} = this.props;
|
||||
|
||||
if (teamId !== currentTeamId) {
|
||||
telemetry.reset();
|
||||
telemetry.start(['team:switch']);
|
||||
}
|
||||
|
||||
StatusBar.setHidden(false, 'slide');
|
||||
requestAnimationFrame(() => {
|
||||
const {actions, closeChannelDrawer, currentTeamId} = this.props;
|
||||
if (teamId !== currentTeamId) {
|
||||
tracker.teamSwitch = Date.now();
|
||||
actions.handleTeamChange(teamId);
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ import {deleteFileCache} from 'app/utils/file';
|
|||
import avoidNativeBridge from 'app/utils/avoid_native_bridge';
|
||||
import {t} from 'app/utils/i18n';
|
||||
import LocalConfig from 'assets/config';
|
||||
import telemetry from 'app/telemetry';
|
||||
|
||||
import App from './app';
|
||||
import './fetch_preconfig';
|
||||
|
|
@ -435,6 +436,11 @@ const handleAppInActive = () => {
|
|||
AppState.addEventListener('change', handleAppStateChange);
|
||||
|
||||
const launchEntry = () => {
|
||||
telemetry.start([
|
||||
'start:select_server_screen',
|
||||
'start:channel_screen',
|
||||
]);
|
||||
|
||||
Navigation.startSingleScreenApp({
|
||||
screen: {
|
||||
screen: 'Entry',
|
||||
|
|
@ -452,6 +458,8 @@ const launchEntry = () => {
|
|||
},
|
||||
animationType: 'fade',
|
||||
});
|
||||
|
||||
telemetry.startSinceLaunch(['start:splash_screen']);
|
||||
};
|
||||
|
||||
configurePushNotifications();
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@ import PushNotifications from 'app/push_notifications';
|
|||
import tracker from 'app/utils/time_tracker';
|
||||
import LocalConfig from 'assets/config';
|
||||
|
||||
import telemetry from 'app/telemetry';
|
||||
|
||||
import ChannelNavBar from './channel_nav_bar';
|
||||
import ChannelPostList from './channel_post_list';
|
||||
|
||||
|
|
@ -104,6 +106,8 @@ export default class Channel extends PureComponent {
|
|||
}
|
||||
|
||||
EventEmitter.emit('renderDrawer');
|
||||
|
||||
telemetry.end(['start:channel_screen']);
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
|
|||
import {preventDoubleTap} from 'app/utils/tap';
|
||||
import {makeStyleSheetFromTheme} from 'app/utils/theme';
|
||||
|
||||
import telemetry from 'app/telemetry';
|
||||
|
||||
import {getUnreadsInCurrentTeam} from 'mattermost-redux/selectors/entities/channels';
|
||||
import {getCurrentTeamId, getTeamMemberships} from 'mattermost-redux/selectors/entities/teams';
|
||||
import EventEmitter from 'mattermost-redux/utils/event_emitter';
|
||||
|
|
@ -69,6 +71,7 @@ class ChannelDrawerButton extends PureComponent {
|
|||
};
|
||||
|
||||
handlePress = preventDoubleTap(() => {
|
||||
telemetry.start(['channel:open_drawer']);
|
||||
this.props.openDrawer();
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ import {
|
|||
View,
|
||||
} from 'react-native';
|
||||
|
||||
import {getLastPostIndex} from 'mattermost-redux/utils/post_list';
|
||||
|
||||
import AnnouncementBanner from 'app/components/announcement_banner';
|
||||
import PostList from 'app/components/post_list';
|
||||
import PostListRetry from 'app/components/post_list_retry';
|
||||
|
|
@ -190,6 +192,7 @@ export default class ChannelPostList extends PureComponent {
|
|||
component = (
|
||||
<PostList
|
||||
postIds={visiblePostIds}
|
||||
lastPostIndex={Platform.OS === 'android' ? getLastPostIndex(visiblePostIds) : -1}
|
||||
extraData={loadMorePostsVisible}
|
||||
onLoadMoreUp={this.loadMorePostsTop}
|
||||
onPostPress={this.goToThread}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,9 @@
|
|||
|
||||
import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {View} from 'react-native';
|
||||
import {Platform, View} from 'react-native';
|
||||
|
||||
import {getLastPostIndex} from 'mattermost-redux/utils/post_list';
|
||||
|
||||
import PostList from 'app/components/post_list';
|
||||
import {makeStyleSheetFromTheme} from 'app/utils/theme';
|
||||
|
|
@ -80,6 +82,7 @@ export default class ChannelPeek extends PureComponent {
|
|||
<View style={style.container}>
|
||||
<PostList
|
||||
postIds={visiblePostIds}
|
||||
lastPostIndex={Platform.OS === 'android' ? getLastPostIndex(visiblePostIds) : -1}
|
||||
renderReplies={true}
|
||||
indicateNewMessages={true}
|
||||
currentUserId={currentUserId}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@ import tracker from 'app/utils/time_tracker';
|
|||
import {t} from 'app/utils/i18n';
|
||||
import {setMfaPreflightDone, getMfaPreflightDone} from 'app/utils/security';
|
||||
|
||||
import telemetry from 'app/telemetry';
|
||||
|
||||
import {RequestStatus} from 'mattermost-redux/constants';
|
||||
|
||||
const mfaExpectedErrors = ['mfa.validate_token.authenticate.app_error', 'ent.mfa.validate_token.authenticate.app_error'];
|
||||
|
|
@ -80,6 +82,8 @@ export default class Login extends PureComponent {
|
|||
}
|
||||
|
||||
goToChannel = () => {
|
||||
telemetry.remove(['start:overall']);
|
||||
|
||||
const {navigator} = this.props;
|
||||
tracker.initialLoad = Date.now();
|
||||
|
||||
|
|
|
|||
|
|
@ -34,6 +34,8 @@ import {preventDoubleTap} from 'app/utils/tap';
|
|||
import tracker from 'app/utils/time_tracker';
|
||||
import {t} from 'app/utils/i18n';
|
||||
|
||||
import telemetry from 'app/telemetry';
|
||||
|
||||
import LocalConfig from 'assets/config';
|
||||
|
||||
export default class SelectServer extends PureComponent {
|
||||
|
|
@ -92,6 +94,9 @@ export default class SelectServer extends PureComponent {
|
|||
|
||||
this.certificateListener = DeviceEventEmitter.addListener('RNFetchBlobCertificate', this.selectCertificate);
|
||||
this.props.navigator.setOnNavigatorEvent(this.handleNavigatorEvent);
|
||||
|
||||
telemetry.end(['start:select_server_screen']);
|
||||
telemetry.save();
|
||||
}
|
||||
|
||||
componentWillUpdate(nextProps, nextState) {
|
||||
|
|
|
|||
6
app/telemetry/index.js
Normal file
6
app/telemetry/index.js
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import telemetry from './telemetry';
|
||||
|
||||
export default telemetry;
|
||||
158
app/telemetry/telemetry.android.js
Normal file
158
app/telemetry/telemetry.android.js
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import LocalConfig from 'assets/config'; // eslint-disable-line
|
||||
|
||||
import {store} from 'app/mattermost';
|
||||
|
||||
import {
|
||||
saveToTelemetryServer,
|
||||
getDeviceInfo,
|
||||
setTraceRecord,
|
||||
} from './telemetry_utils';
|
||||
|
||||
class Telemetry {
|
||||
constructor() {
|
||||
this.appStartTime = 0;
|
||||
this.reactInitializedStartTime = 0;
|
||||
this.reactInitializedEndTime = 0;
|
||||
this.metrics = [];
|
||||
this.currentMetrics = {};
|
||||
this.pendingSinceLaunchMetrics = [];
|
||||
}
|
||||
|
||||
setAppStartTime(startTime) {
|
||||
this.appStartTime = startTime;
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.metrics = [];
|
||||
this.currentMetrics = {};
|
||||
this.pendingSinceLaunchMetrics = [];
|
||||
}
|
||||
|
||||
canSendTelemetry() {
|
||||
const {config} = store.getState().entities.general;
|
||||
return Boolean(!__DEV__ && config.EnableDiagnostics === 'true' && LocalConfig.TelemetryEnabled);
|
||||
}
|
||||
|
||||
start(names = [], time = 0) {
|
||||
if (this.canSendTelemetry()) {
|
||||
const d = new Date();
|
||||
const startTime = d.getTime();
|
||||
|
||||
names.forEach((name) => {
|
||||
this.currentMetrics[name] = {
|
||||
name,
|
||||
startTime: time || startTime,
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
end(names = []) {
|
||||
if (this.canSendTelemetry()) {
|
||||
const d = new Date();
|
||||
const endTime = d.getTime();
|
||||
names.forEach((name) => {
|
||||
const finalMetric = this.currentMetrics[name];
|
||||
|
||||
if (finalMetric && finalMetric.startTime > 0) {
|
||||
this.metrics.push({
|
||||
...finalMetric,
|
||||
endTime,
|
||||
});
|
||||
|
||||
Reflect.deleteProperty(this.currentMetrics, name);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
include(metrics = []) {
|
||||
if (this.canSendTelemetry()) {
|
||||
metrics.forEach((metric) => {
|
||||
this.metrics.push({
|
||||
name: metric.name,
|
||||
startTime: metric.startTime,
|
||||
endTime: metric.endTime,
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
startSinceLaunch(names = []) {
|
||||
if (this.canSendTelemetry()) {
|
||||
const d = new Date();
|
||||
const endTime = d.getTime();
|
||||
|
||||
names.forEach((name) => {
|
||||
if (!this.appStartTime) {
|
||||
this.pendingSinceLaunchMetrics.push({
|
||||
name,
|
||||
endTime,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this.metrics.push({
|
||||
name,
|
||||
startTime: this.appStartTime,
|
||||
endTime,
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
remove(names = []) {
|
||||
names.forEach((name) => {
|
||||
const currentMetric = this.currentMetrics[name];
|
||||
|
||||
if (currentMetric) {
|
||||
Reflect.deleteProperty(this.currentMetrics, name);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
save() {
|
||||
if (!this.canSendTelemetry()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.pendingSinceLaunchMetrics.forEach((pendingMetric) => {
|
||||
this.metrics.push({
|
||||
...pendingMetric,
|
||||
startTime: this.appStartTime,
|
||||
});
|
||||
});
|
||||
|
||||
if (this.metrics.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const metrics = this.metrics.filter((m) => m.endTime && m.startTime).map((metric) => {
|
||||
const {name, startTime, endTime} = metric;
|
||||
let dur;
|
||||
if (endTime && startTime) {
|
||||
dur = (endTime - startTime) * 1000;
|
||||
}
|
||||
|
||||
return setTraceRecord({
|
||||
name,
|
||||
time: startTime * 1000,
|
||||
dur,
|
||||
});
|
||||
});
|
||||
|
||||
const {config} = store.getState().entities.general;
|
||||
const deviceInfo = getDeviceInfo();
|
||||
deviceInfo.serverVersion = config.Version;
|
||||
|
||||
saveToTelemetryServer({trace_events: metrics, device_info: deviceInfo});
|
||||
|
||||
this.reset();
|
||||
}
|
||||
}
|
||||
|
||||
const telemetry = new Telemetry();
|
||||
export default telemetry;
|
||||
34
app/telemetry/telemetry.ios.js
Normal file
34
app/telemetry/telemetry.ios.js
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
class Telemetry {
|
||||
constructor() {
|
||||
this.appStartTime = 0;
|
||||
this.reactInitializedStartTime = 0;
|
||||
this.reactInitializedEndTime = 0;
|
||||
this.metrics = [];
|
||||
this.currentMetrics = {};
|
||||
this.pendingSinceLaunchMetrics = [];
|
||||
}
|
||||
|
||||
setAppStartTime = () => true;
|
||||
|
||||
reset = () => true;
|
||||
|
||||
canSendTelemetry = () => false;
|
||||
|
||||
start = () => true;
|
||||
|
||||
end = () => true;
|
||||
|
||||
include = () => true;
|
||||
|
||||
startSinceLaunch = () => true;
|
||||
|
||||
remove = () => true;
|
||||
|
||||
save = () => true;
|
||||
}
|
||||
|
||||
const telemetry = new Telemetry();
|
||||
export default telemetry;
|
||||
100
app/telemetry/telemetry_utils.js
Normal file
100
app/telemetry/telemetry_utils.js
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {Dimensions} from 'react-native';
|
||||
import DeviceInfo from 'react-native-device-info';
|
||||
|
||||
import LocalConfig from 'assets/config';
|
||||
|
||||
export function saveToTelemetryServer(data) {
|
||||
const {
|
||||
TelemetryEnabled,
|
||||
TelemetryUrl,
|
||||
TelemetryApiKey,
|
||||
} = LocalConfig;
|
||||
|
||||
if (TelemetryEnabled && TelemetryUrl) {
|
||||
fetch(TelemetryUrl, {
|
||||
method: 'post',
|
||||
body: JSON.stringify({data}),
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
'x-api-key': TelemetryApiKey,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const presetTID = {
|
||||
'start:overall': 0,
|
||||
'start:process_packages': 1,
|
||||
'start:content_appeared': 2,
|
||||
'start:select_server_screen': 3,
|
||||
'start:channel_screen': 4,
|
||||
'team:switch': 5,
|
||||
'channel:loading': 6,
|
||||
'channel:switch_loaded': 7,
|
||||
'channel:switch_initial': 8,
|
||||
'channel:close_drawer': 9,
|
||||
'channel:open_drawer': 10,
|
||||
'posts:loading': 11,
|
||||
'posts:list_update': 12,
|
||||
};
|
||||
|
||||
export function setTraceRecord({
|
||||
name,
|
||||
time: ts,
|
||||
dur = 0,
|
||||
instanceKey = 0,
|
||||
pid = 0,
|
||||
}) {
|
||||
const tid = presetTID[name];
|
||||
|
||||
return {
|
||||
cat: 'react-native',
|
||||
ph: 'X',
|
||||
name,
|
||||
ts,
|
||||
dur,
|
||||
pid,
|
||||
tid,
|
||||
args: {
|
||||
instanceKey,
|
||||
tag: name,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function getDeviceInfo() {
|
||||
const {height, width} = Dimensions.get('window');
|
||||
|
||||
return {
|
||||
api_level: DeviceInfo.getAPILevel(),
|
||||
build_number: DeviceInfo.getBuildNumber(),
|
||||
bundle_id: DeviceInfo.getBundleId(),
|
||||
brand: DeviceInfo.getBrand(),
|
||||
country: DeviceInfo.getDeviceCountry(),
|
||||
device_id: DeviceInfo.getDeviceId(),
|
||||
device_locale: DeviceInfo.getDeviceLocale().split('-')[0],
|
||||
device_type: DeviceInfo.getDeviceType(),
|
||||
device_unique_id: DeviceInfo.getUniqueID(),
|
||||
height: height ? Math.floor(height) : 0,
|
||||
is_emulator: DeviceInfo.isEmulator(),
|
||||
is_tablet: DeviceInfo.isTablet(),
|
||||
manufacturer: DeviceInfo.getManufacturer(),
|
||||
max_memory: DeviceInfo.getMaxMemory(),
|
||||
model: DeviceInfo.getModel(),
|
||||
system_name: DeviceInfo.getSystemName(),
|
||||
system_version: DeviceInfo.getSystemVersion(),
|
||||
timezone: DeviceInfo.getTimezone(),
|
||||
app_version: DeviceInfo.getVersion(),
|
||||
width: width ? Math.floor(width) : 0,
|
||||
};
|
||||
}
|
||||
|
||||
export default {
|
||||
getDeviceInfo,
|
||||
setTraceRecord,
|
||||
};
|
||||
|
||||
|
|
@ -42,5 +42,9 @@
|
|||
|
||||
"ShowSentryDebugOptions": false,
|
||||
|
||||
"CustomRequestHeaders": {}
|
||||
"CustomRequestHeaders": {},
|
||||
|
||||
"TelemetryEnabled": false,
|
||||
"TelemetryUrl": "",
|
||||
"TelemetryApiKey": ""
|
||||
}
|
||||
|
|
|
|||
|
|
@ -157,13 +157,6 @@ lane :configure do
|
|||
json['SentryDsnAndroid'] = ENV['SENTRY_DSN_ANDROID']
|
||||
end
|
||||
|
||||
# Configure Telemetry if enabled
|
||||
if ENV['BETA_BUILD'] == 'true' && ENV['TELEMETRY_ENABLED'] == 'true' && ENV['TELEMETRY_URL'] != '' && ENV['TELEMETRY_API_KEY'] != ''
|
||||
json['TelemetryEnabled'] = true
|
||||
json['TelemetryUrl'] = ENV['TELEMETRY_URL']
|
||||
json['TelemetryApiKey'] = ENV['TELEMETRY_API_KEY']
|
||||
end
|
||||
|
||||
# Save the config.json file
|
||||
save_config_json(json)
|
||||
|
||||
|
|
@ -361,6 +354,7 @@ platform :android do
|
|||
lane :build do
|
||||
unless configured
|
||||
configure
|
||||
configure_telemetry_android
|
||||
end
|
||||
update_identifiers
|
||||
replace_assets
|
||||
|
|
@ -383,6 +377,27 @@ platform :android do
|
|||
)
|
||||
end
|
||||
|
||||
desc 'Update config and module/listener for Android telemetry'
|
||||
lane :configure_telemetry_android do
|
||||
if ENV['TELEMETRY_ENABLED'] == 'true' && ENV['TELEMETRY_URL'] != '' && ENV['TELEMETRY_API_KEY'] != ''
|
||||
# Update telemetry config
|
||||
json = load_config_json
|
||||
json['TelemetryEnabled'] = true
|
||||
json['TelemetryUrl'] = ENV['TELEMETRY_URL']
|
||||
json['TelemetryApiKey'] = ENV['TELEMETRY_API_KEY']
|
||||
save_config_json(json)
|
||||
|
||||
beta_dir = './android/app/src/main/java/com/mattermost/rnbeta/'
|
||||
|
||||
# Listen to telemetry react markers
|
||||
find_replace_string(
|
||||
path_to_file: "#{beta_dir}MainApplication.java",
|
||||
old_string: '// addReactMarkerListener();',
|
||||
new_string: 'addReactMarkerListener();'
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
lane :update_identifiers do
|
||||
# Set the name for the app
|
||||
app_name = ENV['APP_NAME'] || 'Mattermost Beta'
|
||||
|
|
|
|||
19
index.js
19
index.js
|
|
@ -2,14 +2,31 @@
|
|||
// See LICENSE.txt for license information.
|
||||
|
||||
import 'react-native/Libraries/Core/InitializeCore';
|
||||
import {AppRegistry, Platform} from 'react-native';
|
||||
import {AppRegistry, DeviceEventEmitter, Platform} from 'react-native';
|
||||
import 'react-native-gesture-handler';
|
||||
|
||||
import LocalConfig from 'assets/config';
|
||||
|
||||
import telemetry from 'app/telemetry';
|
||||
|
||||
import 'app/mattermost';
|
||||
import ShareExtension from 'share_extension/android';
|
||||
|
||||
if (Platform.OS === 'android') {
|
||||
AppRegistry.registerComponent('MattermostShare', () => ShareExtension);
|
||||
|
||||
if (LocalConfig.TelemetryEnabled) {
|
||||
const metricsSubscription = DeviceEventEmitter.addListener('nativeMetrics', (metrics) => {
|
||||
telemetry.setAppStartTime(metrics.appReload);
|
||||
telemetry.include([
|
||||
{name: 'start:process_packages', startTime: metrics.processPackagesStart, endTime: metrics.processPackagesEnd},
|
||||
{name: 'start:content_appeared', startTime: metrics.appReload, endTime: metrics.appContentAppeared},
|
||||
]);
|
||||
telemetry.start(['start:overall'], metrics.appReload);
|
||||
|
||||
DeviceEventEmitter.removeSubscription(metricsSubscription);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Uncomment the snippet below if you want to update the modules
|
||||
|
|
|
|||
55
package-lock.json
generated
55
package-lock.json
generated
|
|
@ -2700,6 +2700,7 @@
|
|||
"resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
|
||||
"integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"is-extendable": "^0.1.0"
|
||||
}
|
||||
|
|
@ -2744,7 +2745,8 @@
|
|||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
||||
"integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"is-glob": {
|
||||
"version": "4.0.0",
|
||||
|
|
@ -4821,7 +4823,8 @@
|
|||
},
|
||||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"aproba": {
|
||||
"version": "1.2.0",
|
||||
|
|
@ -4839,11 +4842,13 @@
|
|||
},
|
||||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
|
|
@ -4856,15 +4861,18 @@
|
|||
},
|
||||
"code-point-at": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"console-control-strings": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
|
|
@ -4967,7 +4975,8 @@
|
|||
},
|
||||
"inherits": {
|
||||
"version": "2.0.3",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.5",
|
||||
|
|
@ -4977,6 +4986,7 @@
|
|||
"is-fullwidth-code-point": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"number-is-nan": "^1.0.0"
|
||||
}
|
||||
|
|
@ -4989,17 +4999,20 @@
|
|||
"minimatch": {
|
||||
"version": "3.0.4",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
},
|
||||
"minimist": {
|
||||
"version": "0.0.8",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"minipass": {
|
||||
"version": "2.3.5",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"safe-buffer": "^5.1.2",
|
||||
"yallist": "^3.0.0"
|
||||
|
|
@ -5016,6 +5029,7 @@
|
|||
"mkdirp": {
|
||||
"version": "0.5.1",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"minimist": "0.0.8"
|
||||
}
|
||||
|
|
@ -5088,7 +5102,8 @@
|
|||
},
|
||||
"number-is-nan": {
|
||||
"version": "1.0.1",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
|
|
@ -5098,6 +5113,7 @@
|
|||
"once": {
|
||||
"version": "1.4.0",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
|
|
@ -5173,7 +5189,8 @@
|
|||
},
|
||||
"safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
|
|
@ -5203,6 +5220,7 @@
|
|||
"string-width": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"code-point-at": "^1.0.0",
|
||||
"is-fullwidth-code-point": "^1.0.0",
|
||||
|
|
@ -5220,6 +5238,7 @@
|
|||
"strip-ansi": {
|
||||
"version": "3.0.1",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
|
|
@ -5258,11 +5277,13 @@
|
|||
},
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"yallist": {
|
||||
"version": "3.0.3",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -9839,8 +9860,8 @@
|
|||
"integrity": "sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A=="
|
||||
},
|
||||
"mattermost-redux": {
|
||||
"version": "github:mattermost/mattermost-redux#b854353ee96b875b6b87857932b83b1143131860",
|
||||
"from": "github:mattermost/mattermost-redux#b854353ee96b875b6b87857932b83b1143131860",
|
||||
"version": "github:mattermost/mattermost-redux#0df3ec0182e9d9ec67397157611d887f5fbc1bf1",
|
||||
"from": "github:mattermost/mattermost-redux#0df3ec0182e9d9ec67397157611d887f5fbc1bf1",
|
||||
"requires": {
|
||||
"deep-equal": "1.0.1",
|
||||
"eslint-plugin-header": "3.0.0",
|
||||
|
|
@ -13122,7 +13143,8 @@
|
|||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
|
||||
"integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"braces": {
|
||||
"version": "2.3.2",
|
||||
|
|
@ -13408,7 +13430,8 @@
|
|||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
|
||||
"integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"micromatch": {
|
||||
"version": "3.1.10",
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
"intl": "1.2.5",
|
||||
"jail-monkey": "2.0.0",
|
||||
"jsc-android": "236355.1.1",
|
||||
"mattermost-redux": "github:mattermost/mattermost-redux#b854353ee96b875b6b87857932b83b1143131860",
|
||||
"mattermost-redux": "github:mattermost/mattermost-redux#0df3ec0182e9d9ec67397157611d887f5fbc1bf1",
|
||||
"mime-db": "1.40.0",
|
||||
"moment-timezone": "0.5.23",
|
||||
"prop-types": "15.7.2",
|
||||
|
|
|
|||
|
|
@ -207,6 +207,9 @@ module.exports = [
|
|||
'app/store/index.js',
|
||||
'app/store/middleware.js',
|
||||
'app/store/thunk.js',
|
||||
'app/telemetry/index.js',
|
||||
'app/telemetry/telemetry.android.js',
|
||||
'app/telemetry/telemetry_utils.js',
|
||||
'app/utils/avoid_native_bridge.js',
|
||||
'app/utils/client_upgrade.js',
|
||||
'app/utils/general.js',
|
||||
|
|
|
|||
|
|
@ -179,6 +179,9 @@ module.exports = ['./node_modules/app/app.js',
|
|||
'./node_modules/app/store/index.js',
|
||||
'./node_modules/app/store/middleware.js',
|
||||
'./node_modules/app/store/thunk.js',
|
||||
'./node_modules/app/telemetry/index.js',
|
||||
'./node_modules/app/telemetry/telemetry.android.js',
|
||||
'./node_modules/app/telemetry/telemetry_utils.js',
|
||||
'./node_modules/app/utils/avoid_native_bridge.js',
|
||||
'./node_modules/app/utils/client_upgrade.js',
|
||||
'./node_modules/app/utils/general.js',
|
||||
|
|
|
|||
Loading…
Reference in a new issue