Add java docs to the code and adding some same code

This commit is contained in:
Vishal Ratna 2021-12-11 13:55:56 +05:30
Родитель 6d25cb14cd
Коммит 0e0dbc7c26
15 изменённых файлов: 288 добавлений и 41 удалений

Просмотреть файл

@ -0,0 +1,89 @@
package com.microsoft.sample.custom;
import android.util.Log;
import androidx.annotation.NonNull;
import com.microsoft.snippet.ExecutionContext;
import com.microsoft.snippet.Snippet;
import com.microsoft.snippet.token.ExtendableLogToken;
import com.microsoft.snippet.token.ILogToken;
/**
* Demo for showing custom implementation of Execution Path. This path, takes the data that is
* captured from the MeasuredExecutionPath and passes it to FileExecutionPath and then the data
* is written to the file.
* <p>
* We need to override LogToken also, as for the code that is non contiguous all the measurements
* are inside the log token that is handed over to the user by Snippet.startCapture() API
* So if we want that our new execution to work for both kinds of APIs that ie.
* The one passed through a lambda in Snippet.capture(lambda) and Snippet.startCapture()/LogToken.endCapture()
* We need to override both the classes.
*/
public class FileExecutionPath extends Snippet.MeasuredExecutionPath {
@Override
public ILogToken startCapture(String tag) {
return super.startCapture(tag);
}
@NonNull
@Override
public ExecutionContext capture(String message, Snippet.Closure closure) {
ExecutionContext context = super.capture(message, closure);
Log.d("Snippet", "Class: " + context.getClassName() + "Duration: " + context.getExecutionDuration());
// Context has all the information that measured path has captured. Use that to write to files.
return writeToFile(context);
}
private ExecutionContext writeToFile(ExecutionContext context) {
// Code to write to a file goes here, create a thread and write.
// Finally return a the execution context(could be the same or a new implementation) with some
// of the details that you captured.
// NOTE: always put the relevant information on the context before you start doing IO
// so that the execution path could return successfully.
return context;
}
@NonNull
@Override
public ExecutionContext capture(Snippet.Closure closure) {
return super.capture(closure);
}
// We need to return a log token implementation that writes to a file when we call endCapture()
// APIs.
// USE ExtendableLogToken for the above purpose
@Override
public ILogToken startCapture() {
return new ExtendableLogToken(super.startCapture());
}
@Override
public ILogToken find(String tag) {
return super.find(tag);
}
public class FileWritingLogToken extends ExtendableLogToken {
public FileWritingLogToken(ILogToken logToken) {
super(logToken);
}
@Override
public ExecutionContext endCapture(String message) {
ExecutionContext context = super.endCapture(message);
writeToFile(context);
return context;
}
@Override
public ExecutionContext endCapture() {
ExecutionContext context = super.endCapture();
writeToFile(context);
return context;
}
}
}

Просмотреть файл

@ -1,8 +1,10 @@
package com.microsoft.snippet;
/**
* POJO Alert!
* Wraps all the information that can be returned through the library.
* If any other information is needed it can be added ad-hoc.
* Custom implementation of {@link ExecutionPath} might/might not have to extend this to
* accommodate any additional information.
*/
public class ExecutionContext {
private String mClass;

Просмотреть файл

@ -7,6 +7,7 @@ package com.microsoft.snippet;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
import com.microsoft.snippet.token.ILogToken;
import com.microsoft.snippet.token.LogTokenState;
@ -17,7 +18,10 @@ import java.util.List;
/**
* LogToken Pool is helps recycling the log token objects that are used by Snippet.
* Currently there is no upper cap on the no of tokens it could hold. But we are trying to understand
* what could be the upper limit.
*/
@RestrictTo(RestrictTo.Scope.LIBRARY)
public final class LogTokenPool {
private static final String TAG = LogTokenPool.class.getSimpleName();

Просмотреть файл

@ -6,6 +6,8 @@ package com.microsoft.snippet;
import android.util.Log;
import androidx.annotation.RestrictTo;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
@ -18,6 +20,7 @@ import java.util.concurrent.atomic.AtomicReference;
* @param <T>
* @author vishalratna
*/
@RestrictTo(RestrictTo.Scope.LIBRARY)
public final class OneShot<T> {
private static final String TAG = OneShot.class.getSimpleName();

Просмотреть файл

@ -4,11 +4,14 @@
package com.microsoft.snippet;
import androidx.annotation.RestrictTo;
/**
* Represents a Pair of objects
* @param <A> A
* @param <B> B
*/
@RestrictTo(RestrictTo.Scope.LIBRARY)
public final class Pair<A, B> {
A a;
B b;

Просмотреть файл

@ -12,6 +12,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.microsoft.snippet.token.AttenuatedLogToken;
import com.microsoft.snippet.token.ExtendableLogToken;
import com.microsoft.snippet.token.ILogToken;
import com.microsoft.snippet.token.LogTokenState;
@ -27,12 +28,21 @@ import java.util.Locale;
* printing the logs. The work is tedious and it becomes a nightmare when we have to do it across the
* code base. The API provided by this library could be used to monitor PRs in the PR reviews where
* the reviewer could ask for numbers for a specific snippet and compare the before and after numbers.
* It could also be used as a general library to print code execution duration.
* <p>
* The important thing to note is we can configure the code in such a way that we do not have to
* care about the additional verbosity in the release builds as it automatically makes the code
* no-op in the release builds or any other builds if we want. We also have an option to configure the library
* differently in different builds. Please check {@link Snippet#install(ExecutionPath)} API.
*
* <h2> There are 2 ways to measure time in Snippet</h2>
* <ol>
* <li> Using <code>capture</code> APIs of Snippet</li>
* <li> Using log tokens and <code>startCapture</code> and <code>endCapture</code> APIs </li>
* </ol>
* <p>
* Snippet's capture() API accepts a lambda and measures the time it takes to execute it and logs it
* on the log cat.
* Example usage with <code>Snippet.capture</code>:
* <pre>
* {@code
@ -64,10 +74,12 @@ import java.util.Locale;
* Here library is going to measure time used to execute the code represented by the underlying lambda.
* <p>
* When you intent to measure the execution times for code which is not spread across multiple
* methods and classes, capture APIs can be used. There is one caveat though, while capturing, we pass
* methods and classes, capture() API can be used. There is one caveat though, while capturing, we pass
* a lambda for the closure representing the code snippet, this might be a problem if we want to capture
* some non final variables outside the scope of lambda(Java does not support that). For that use, {@link Final} to create a
* wrapper around your variable and use {@link Final#get()} and {@link Final#set(Object)}()} methods.
* This is tedious though, but if you want to use the capture based approach this is the way out. The new
* approach for this use case is described in the next section.
* <p>
* Another approach is through {@link Snippet#startCapture()} & {@link LogToken#endCapture()} APIs.
* <code>startCapture</code> is going to return a token representing your execution it can be passed
@ -97,7 +109,7 @@ import java.util.Locale;
* </pre>
* We can use <code>startCapture()</code> method with a TAG also. This is particularly useful when
* the execution is spread across multiple classes. We can use, {@link Snippet#find(String)} to find
* the log token that was created with this tag. The logtoken received can be then used normally.
* the log token that was created with this tag. The log token received can be then used normally.
* {@link LogToken} objects are internally obtained through a pool so, Snippet tries it best to
* recycle the objects again and again.
* <p>
@ -106,7 +118,7 @@ import java.util.Locale;
* While this is global filter for all the logs, you can still choose to have a different filter for a
* particular LogToken using {@link LogToken#overrideFilter(String)} which will override the global filter.
* <p>
* Snippet can print Class, Method and Line number information in the logs as a part of execution
* Snippet can print class, method and line number information in the logs as a part of execution
* context. By default it prints class and method name. You can choose to have your combination of
* details through {@link Snippet#addFlag(int)}
* <b>Valid options are:</b>
@ -133,20 +145,52 @@ import java.util.Locale;
* {@link LogToken#isThreadLockEnabled()} can be used.
* </p>
*
* <p>
* <p> There is a interesting concept of splits:
* There are times while you are measuring a sequence of code and you would like to measure how
* much time some steps take within that sequence. As an example, while you are measuring some
* method that takes 300ms to execute, it could have multiple areas inside it that could be adding up
* to that number. So, to measure that {@link LogToken#addSplit()} and {@link LogToken#addSplit(String)}
* could be used. Each call to add split print the time takes since the last addSplit() was called.
* If addSplit() is called for the first time, then it would measure the time from startCapture().
* Once the endCapture() is called and there are {@link Split} inside your capture, snippet also prints
* a clean split summary that shows what was the fraction of time each split took to give an overall idea to
* the user.
* </p>
*
* <p>
* Snippet also introduces a concept of {@link ExecutionPath}. It is a routing mechanism where, it
* tell the library the way to route its code. Whenever an Snippet API is called, it relays it to an execution path.
* That directs it to the core library functionality. So, this provides a capability where the user can
* provide custom execution path implementations and make Snippet run their own code. It also provides a
* mechanism to run different execution paths in different build types. Just add a check for your build type and
* install the execution path that you want using {@link Snippet#install(ExecutionPath)} method.
* <p>
* If your aim is to do the measurement then {@link MeasuredExecutionPath} is already shipped with Snippet.
* Any additional work could be added by extending {@link MeasuredExecutionPath}.
*
* <b> Steps to implement a custom path. See FileExecutionPath in the github sample app for a demo.</b>
* 1. Extend ExecutionPath, in our example we will extend MeasuredExecutionPath.
* 2. Override {@link ExecutionPath#capture(Closure)} and {@link ExecutionPath#capture(String, Closure)}
* This will make sure that you are implementing a custom code for lambda based API.
* 3. There are non-contiguous areas of code also, to address that we have {@link Snippet#startCapture()}
* {@link Snippet#startCapture(String)} APIs, that returns a log token, so if you are writing a new execution path
* you need to provide a custom log token also and the {@link LogToken#endCapture()} and {@link LogToken#endCapture(String)}
* so that you can perform the custom actions on all types of code. For doing this extend
* {@link com.microsoft.snippet.token.ExtendableLogToken} and override
* {@link com.microsoft.snippet.token.ExtendableLogToken#endCapture(String)}, and
* {@link ExtendableLogToken#endCapture()}. Once done, return the ExtendableLogToken instance from
* {@link Snippet#startCapture(String)}, and {@link Snippet#startCapture(String)} methods.
*
* NOTE: In almost all the cases, every new execution path that would be created, would require a new
* extension of {@link ExtendableLogToken}
* </p>
*
* @author vishalratna
*/
public final class Snippet {
public static final AttenuatedLogToken NO_OP_TOKEN = new AttenuatedLogToken();
private static final String TAG = LogToken.class.getSimpleName();
static final AttenuatedLogToken NO_OP_TOKEN = new AttenuatedLogToken();
private static final ExecutionContext EMPTY_CONTEXT = new ExecutionContext();
public static final int FLAG_METADATA_CLASS = 1 << 31;
public static final int FLAG_METADATA_METHOD = 1 << 30;
@ -514,7 +558,7 @@ public final class Snippet {
this.mFilter = Snippet.primaryFilter;
this.mThreadId = -1L;
this.mThreadLockEnabled = false;
if(this.mSplitRecord != null) {
if (this.mSplitRecord != null) {
this.mSplitRecord.clear();
}
this.mSplitRecord = null;

Просмотреть файл

@ -1,32 +0,0 @@
/*
* Copyright © Microsoft Corporation. All rights reserved.
*/
package com.microsoft.snippet;
/**
* Contains the list of areas that are tracked by Snippet. These will help getting the before/after
* numbers while doing the PR review.
*/
public final class SnippetConstants {
private SnippetConstants() {
// private
}
// TTE stands for Time to Execute
public static final String SUPER_ON_MAM_CREATE = "TTE super.onMamCreate()";
public static final String INIT_SKYPE_DB_HELPER = "TTE SkypeTeamsDBHelper";
public static final String INIT_BACKGROUND_OBJ = "TTE Initialize background objects";
public static final String INIT_NOTIFICATION_CHANNEL_HELPER = "TTE Initialize notificationChannelHelper";
public static final String INIT_NOTIFICATION_MGR = "TTE Initialize notification manager";
public static final String SUPER_MA_ON_CREATE = "TTE super.onCreate() MA";
public static final String INIT_SEARCH_BAR_VIEW = "TTE SearchBarView MA";
public static final String SETUP_CONTENT_VIEW = "TTE SetupContentView";
public static final String INJECT_DEPENDENCIES = "TTE injectIfNecessary";
public static final String SETUP_TOOLBAR = "TTE setupToolBar";
public static final String MA_LOAD_INACTIVE_TABS_UI_THREAD_WORK = "TTE loadInactiveTabs() UI thread Work";
public static final String MA_LOAD_SELECTED_FRAGMENT = "TTE load selected fragment";
public static final String DEPENDENCY_INJECTION = "TTE Dependency injection";
public static final String BOTTOM_BAR_APP_CLICK_TELEMETRY = "Bottom Bar App Click Telemetry";
}

Просмотреть файл

@ -1,7 +1,19 @@
package com.microsoft.snippet;
import androidx.annotation.RestrictTo;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Split is a sub section of code within a capture( a contiguous/non-contiguous section of code).
* It is used to measure the duration of subsections and helps in double clicking the areas that are
* are creating problems.
*
* It is advised to use this only for debugging and investigation purposes. Though in release execution path
* all these {@link Snippet.LogToken#addSplit()} calls are noop and none will be coming to your release builds.
* But according our understanding it should not be shipped to production. It would make your code look dirty!
*/
@RestrictTo(RestrictTo.Scope.LIBRARY)
public class Split {
private static final AtomicInteger SEQUENCE = new AtomicInteger(1);

Просмотреть файл

@ -4,11 +4,14 @@
package com.microsoft.snippet;
import androidx.annotation.RestrictTo;
/**
* Internal helper class used to extract the execution context of the code which was guarded
* by Snippet APIs.
* NOT FOR EXTERNAL USE
*/
@RestrictTo(RestrictTo.Scope.LIBRARY)
final class StackAnalyser {
static final int API_CAPTURE = 0;
static final int API_LOG_TOKEN = 1;

Просмотреть файл

@ -7,6 +7,7 @@ package com.microsoft.snippet;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
import com.microsoft.snippet.token.ILogToken;
@ -20,6 +21,7 @@ import java.util.Set;
*
* @author vishalratna
*/
@RestrictTo(RestrictTo.Scope.LIBRARY)
class TagHelper implements ILogTokenSearcher {
private static final String LOG_TAG = TagHelper.class.getSimpleName();

Просмотреть файл

@ -6,11 +6,14 @@ package com.microsoft.snippet;
import android.os.SystemClock;
import androidx.annotation.RestrictTo;
import com.microsoft.snippet.token.ILogToken;
/**
* Utility class the houses some helper functions used across the library.
*/
@RestrictTo(RestrictTo.Scope.LIBRARY)
final class ToolBox {
private ToolBox() {

Просмотреть файл

@ -4,6 +4,8 @@
package com.microsoft.snippet;
import androidx.annotation.RestrictTo;
/**
* Represents a Triple of objects
*
@ -11,6 +13,7 @@ package com.microsoft.snippet;
* @param <B> B
* @param <C> C
*/
@RestrictTo(RestrictTo.Scope.LIBRARY)
public final class Triple<A, B, C> {
A a;
B b;

Просмотреть файл

@ -4,6 +4,8 @@
package com.microsoft.snippet.token;
import androidx.annotation.RestrictTo;
import com.microsoft.snippet.ExecutionContext;
/**
@ -18,6 +20,7 @@ import com.microsoft.snippet.ExecutionContext;
* some activity. As application onCreate() will not be called always, activity might need a
* null check after find call, that is avoided by the use of this class.
*/
@RestrictTo(RestrictTo.Scope.LIBRARY)
public final class AttenuatedLogToken implements ILogToken {
private static final ExecutionContext NONE_INFO = new ExecutionContext();

Просмотреть файл

@ -0,0 +1,107 @@
package com.microsoft.snippet.token;
import com.microsoft.snippet.ExecutionContext;
import com.microsoft.snippet.Snippet;
/**
* A decorator over the {@link ILogToken} API that accepts a log token and routes all the calls
* from the inner token that is used for creation of this class.
* This could be potentially used for scenarios where you have to write a custom execution path
* implementation and {@link com.microsoft.snippet.Snippet#startCapture(String)} and {@link Snippet#startCapture()}
* return a log token that contains the {@link ExecutionContext}.
* Using this token we can use the returned execution context information and do additional work that
* we need to perform.
*/
public class ExtendableLogToken implements ILogToken {
private final ILogToken mSnippetToken;
public ExtendableLogToken(ILogToken logToken) {
this.mSnippetToken = logToken;
}
@Override
public ExecutionContext endCapture(String message) {
return mSnippetToken.endCapture(message);
}
@Override
public ExecutionContext endCapture() {
return mSnippetToken.endCapture();
}
@Override
public final long getStart() {
return mSnippetToken.getStart();
}
@Override
public final long getEnd() {
return mSnippetToken.getEnd();
}
@Override
public final void setStart(long start) {
mSnippetToken.setStart(start);
}
@Override
public final void setEnd(long start) {
mSnippetToken.setEnd(start);
}
@Override
public final ILogToken overrideFilter(String newFilter) {
return mSnippetToken.overrideFilter(newFilter);
}
@Override
public final String filter() {
return mSnippetToken.filter();
}
@Override
public final long creatorThreadId() {
return mSnippetToken.creatorThreadId();
}
@Override
public final boolean isThreadLockEnabled() {
return mSnippetToken.isThreadLockEnabled();
}
@Override
public final ILogToken enableThreadLock() {
return mSnippetToken.enableThreadLock();
}
@Override
public final void setCreatorThreadId(long threadId) {
mSnippetToken.setCreatorThreadId(threadId);
}
@Override
public final void reset() {
mSnippetToken.reset();
}
@Override
public final void addSplit() {
mSnippetToken.addSplit();
}
@Override
public final void addSplit(String message) {
mSnippetToken.addSplit(message);
}
@Override
public final void setState(LogTokenState state) {
mSnippetToken.setState(state);
}
@Override
public final LogTokenState getState() {
return mSnippetToken.getState();
}
}

Просмотреть файл

@ -34,14 +34,15 @@ public interface ILogToken {
ExecutionContext endCapture(String message);
/**
* ThreadLock: It is a restriction that could be implemented on a log token level where the user
* could say that the thread that starts the measurement should only be the one that ends the measurement.
* Returns the id of the thread that asked for this log token.
*
* @return Thread ID.
*/
long creatorThreadId();
/**
*
* Returns whether a thread lock is enabled or not.
*/
boolean isThreadLockEnabled();