Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ build/
sourcerer-app.iml
sourcerer-app.ipr
sourcerer-app.iws
app.iml
app.ipr
app.iws
/confluence/target
/dependencies
/dist
Expand All @@ -16,4 +19,4 @@ sourcerer-app.iws
/ultimate/dependencies
/ultimate/ideaSDK
/ultimate/out
/ultimate/tmp
/ultimate/tmp
10 changes: 9 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -39,21 +39,28 @@ buildConfig {
apiBasePath = 'https://sourcerer.io/api/commit'
}
apiBasePath = project.hasProperty('api') ? api : apiBasePath

buildConfigField 'String', 'API_BASE_PATH', apiBasePath

// Common.
buildConfigField 'String', 'ENVIRONMENT', ext.environment
buildConfigField 'String', 'PROFILE_URL', 'https://sourcerer.io/'

// App version.
buildConfigField 'int', 'VERSION_CODE', '1'
buildConfigField 'String', 'VERSION', '0.0.1'

// Logging.
buildConfigField 'int', 'LOG_LEVEL', '3'

// Google Analytics.
buildConfigField 'String', 'GA_BASE_PATH', 'http://www.google-analytics.com'
buildConfigField 'String', 'GA_TRACKING_ID', 'UA-107129190-2'
buildConfigField 'boolean', 'IS_GA_ENABLED', 'true'

// Logging.
buildConfigField 'String', 'SENTRY_DSN', 'https://0263d6473bd24a9ba40e25aa5fb0a242:c5451dc815074bff8ce3fb9f0851f2f5@sentry.io/233260'
buildConfigField 'boolean', 'PRINT_STACK_TRACE', 'true'

buildConfig
}

Expand Down Expand Up @@ -89,6 +96,7 @@ dependencies {
version: '4.8.0.201706111038-r'
compile "org.slf4j:slf4j-nop:1.7.2"
compile 'org.jpmml:pmml-evaluator:1.3.9'
compile 'io.sentry:sentry:1.6.0'

testCompile 'org.jetbrains.kotlin:kotlin-test'
testCompile 'org.jetbrains.spek:spek-api:1.1.4'
Expand Down
36 changes: 4 additions & 32 deletions src/main/kotlin/app/Analytics.kt
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ object Analytics {
* - t: Hit Type - type of event
* - dp: Document Path - virtual url
*/
private fun trackEvent(event: String, params: List<Param> = listOf()) {
fun trackEvent(event: String, params: List<Param> = listOf()) {
if (!IS_ENABLED || (username.isEmpty() && uuid.isEmpty())) {
return
}
Expand All @@ -75,44 +75,16 @@ object Analytics {
post(params + defaultParams.filter { !params.contains(it) }
+ idParams).responseString()
} catch (e: Throwable) {
Logger.error("Error while sending error report", e, logOnly = true)
Logger.error(e, "Error while sending GA report", logOnly = true)
}
}

fun trackStart() {
trackEvent("start")
}

fun trackAuth() {
trackEvent("auth")
}

fun trackConfigSetup() {
trackEvent("config/setup")
}

fun trackConfigChanged() {
trackEvent("config/changed")
}

fun trackHashingRepoSuccess() {
trackEvent("hashing/repo/success")
}

fun trackHashingSuccess() {
trackEvent("hashing/success")
}

fun trackError(e: Throwable? = null, code: String = "") {
val url = if (e != null) getErrorUrl(e) else code
fun trackError(e: Throwable? = null) {
val url = if (e != null) getErrorUrl(e) else ""
val separator = if (url.isNotEmpty()) "/" else ""
trackEvent("error" + separator + url, listOf("t" to HIT_EXCEPTION))
}

fun trackExit() {
trackEvent("exit")
}

private fun getErrorUrl(e: Throwable): String {
// Mapping for request exceptions.
when (e) {
Expand Down
128 changes: 113 additions & 15 deletions src/main/kotlin/app/Logger.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,69 +3,133 @@

package app

import io.sentry.Sentry
import io.sentry.context.Context
import io.sentry.event.Breadcrumb
import io.sentry.event.UserBuilder
import io.sentry.event.BreadcrumbBuilder
import java.util.*


/**
* Singleton class that logs events of different levels.
*/
object Logger {
object Events {
val START = "start"
val AUTH = "auth"
val CONFIG_SETUP = "config/setup"
val CONFIG_CHANGED = "config/changed"
val HASHING_REPO_SUCCESS = "hashing/repo/success"
val HASHING_SUCCESS = "hashing/success"
val EXIT = "exit"
}

/**
* Current log level. All that higher than this level will not be displayed.
*/
const val LEVEL = 3
private const val LEVEL = BuildConfig.LOG_LEVEL

/**
* Error level.
*/
const val ERROR = 0
private const val ERROR = 0

/**
* Warning level.
*/
const val WARN = 1
private const val WARN = 1

/**
* Information level.
*/
const val INFO = 2
private const val INFO = 2

/**
* Debug level.
*/
const val DEBUG = 3
private const val DEBUG = 3

/**
* Trace level. For extremely detailed and high volume debug logs.
*/
private const val TRACE = 4

/**
* Print stack trace on error log.
*/
private const val PRINT_STACK_TRACE = BuildConfig.PRINT_STACK_TRACE

/**
* Context of Sentry error reporting software for adding info.
*/
private val sentryContext: Context

/**
* Username used for error reporting.
*/
var username: String? = null
set(value) {
sentryContext.user = UserBuilder().setUsername(value).build()
Analytics.username = value ?: ""
}

var uuid: String? = null
set(value) {
Analytics.uuid = value ?: ""
}

init {
Sentry.init(BuildConfig.SENTRY_DSN)
sentryContext = Sentry.getContext()
addTags()
}

/**
* Log error message with exception info.
* Don't log private information with this method.
*
* @property message the message for user and logs.
* @property e the exception if presented.
* @property code the code of error if exception is not presented.
* @property message the message for user and logs.
* @property logOnly only log to console, no additional actions.
*/
fun error(message: String, e: Throwable? = null, code: String = "",
logOnly: Boolean = false) {
fun error(e: Throwable, message: String = "", logOnly: Boolean = false) {
val finalMessage = if (message.isNotBlank()) { message + ": " }
else { "" } + e.message
if (LEVEL >= ERROR) {
println("[e] $message" + if (e != null) ": $e" else "")
println("[e] $finalMessage")
if (PRINT_STACK_TRACE) {
e.printStackTrace()
}
}
if (!logOnly) {
Analytics.trackError(e = e, code = code)
//TODO(anatoly): Add error tracking software.
Analytics.trackError(e)
Sentry.capture(e)
}
addBreadcrumb(finalMessage, Breadcrumb.Level.ERROR)
}

/**
* Log warning message.
* Log warning message. Don't log private information with this method.
*/
fun warn(message: String) {
if (LEVEL >= WARN) {
println("[w] $message.")
}
addBreadcrumb(message, Breadcrumb.Level.WARNING)
}

/**
* Log information message.
* Log information message. Don't log private information with this method.
*/
fun info(message: String) {
fun info(message: String, event: String = "") {
if (LEVEL >= INFO) {
println("[i] $message.")
}
if (event.isNotBlank()) {
Analytics.trackEvent(event)
}
addBreadcrumb(message, Breadcrumb.Level.INFO)
}

/**
Expand All @@ -76,4 +140,38 @@ object Logger {
println("[d] $message.")
}
}

/**
* Log trace message.
*/
fun trace(message: String) {
if (LEVEL >= TRACE) {
println("[t] $message.")
}
}

private fun addBreadcrumb(message: String, level: Breadcrumb.Level) {
sentryContext.recordBreadcrumb(BreadcrumbBuilder()
.setMessage(message)
.setLevel(level)
.setTimestamp(Date())
.build())
}

private fun addTags() {
val default = "unavailable"
val osName = System.getProperty("os.name", default)
val osVersion = System.getProperty("os.version", default)
val javaVendor = System.getProperty("java.vendor", default)
val javaVersion = System.getProperty("java.version", default)

sentryContext.addTag("environment", BuildConfig.ENVIRONMENT)
sentryContext.addTag("version", BuildConfig.VERSION)
sentryContext.addTag("version-code", BuildConfig.VERSION_CODE
.toString())
sentryContext.addTag("os-name", osName)
sentryContext.addTag("os-version", osVersion)
sentryContext.addTag("java-vendor", javaVendor)
sentryContext.addTag("java-version", javaVersion)
}
}
27 changes: 11 additions & 16 deletions src/main/kotlin/app/Main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ import com.beust.jcommander.JCommander
import com.beust.jcommander.MissingCommandException

fun main(argv : Array<String>) {
Thread.setDefaultUncaughtExceptionHandler { _, e: Throwable? ->
Logger.error("Uncaught exception", e)
Thread.setDefaultUncaughtExceptionHandler { _, e: Throwable ->
Logger.error(e, "Uncaught exception")
}
Main(argv)
}
Expand All @@ -30,8 +30,8 @@ class Main(argv: Array<String>) {
private val api = ServerApi(configurator)

init {
Analytics.uuid = configurator.getUuidPersistent()
Analytics.trackStart()
Logger.uuid = configurator.getUuidPersistent()
Logger.info("App started", Logger.Events.START)

val options = Options()
val commandAdd = CommandAdd()
Expand Down Expand Up @@ -64,13 +64,10 @@ class Main(argv: Array<String>) {
else -> startUi()
}
} catch (e: MissingCommandException) {
Logger.error(
message = "No such command: ${e.unknownCommand}",
code = "no-command"
)
Logger.warn("No such command: ${e.unknownCommand}")
}

Analytics.trackExit()
Logger.info("App finished", Logger.Events.EXIT)
}

private fun startUi() {
Expand All @@ -86,19 +83,17 @@ class Main(argv: Array<String>) {
configurator.saveToFile()
println("Added git repository at $path.")

Analytics.trackConfigChanged()
Logger.info("Config changed", Logger.Events.CONFIG_CHANGED)
} else {
Logger.error(message = "No valid git repository found at $path.",
code = "repo-invalid")
Logger.warn("No valid git repository found at specified path")
}
}

private fun doConfig(commandOptions: CommandConfig) {
val (key, value) = commandOptions.pair

if (!arrayListOf("username", "password").contains(key)) {
Logger.error(message = "No such key $key",
code = "invalid-params")
Logger.warn("No such key $key")
return
}

Expand All @@ -109,7 +104,7 @@ class Main(argv: Array<String>) {

configurator.saveToFile()

Analytics.trackConfigChanged()
Logger.info("Config changed", Logger.Events.CONFIG_CHANGED)
}

private fun doList() {
Expand All @@ -126,7 +121,7 @@ class Main(argv: Array<String>) {
configurator.saveToFile()
println("Repository removed from tracking list.")

Analytics.trackConfigChanged()
Logger.info("Config changed", Logger.Events.CONFIG_CHANGED)
} else {
println("Repository not found in tracking list.")
}
Expand Down
3 changes: 0 additions & 3 deletions src/main/kotlin/app/api/ServerApi.kt
Original file line number Diff line number Diff line change
Expand Up @@ -118,14 +118,11 @@ class ServerApi (private val configurator: Configurator) : Api {
Logger.debug("Request $requestName success")
return parser(res.data)
} else {
Logger.error("Request $requestName error", e)
throw RequestException(e)
}
} catch (e: InvalidProtocolBufferException) {
Logger.error("Request $requestName error while parsing", e)
throw RequestException(e)
} catch (e: InvalidParameterException) {
Logger.error("Request $requestName error while parsing", e)
throw RequestException(e)
}
}
Expand Down
Loading