我正在将New Relic集成到我的项目中(使用Android Studio& Gradle),该项目有2个版本。每个变体都有自己生成的令牌,我将其存储在每个变体的string.xml
文件中。
在New Relic documentation中,它说明了以下内容:
在项目的根目录(projectname / app)中,添加一个newrelic.properties文件,其中包含以下行:
com.newrelic.application_token=generated_token
问题是,如果我这样做,如何为正确的变体显示正确的标记?如果此文件必须出现在项目根目录中,则我无法为每个变体创建一个,因此我不得不对两个变体使用相同的标记,这对我的要求不起作用。< / p>
任何见解都将受到赞赏。
答案 0 :(得分:1)
好的,所以在联系New Relic的支持团队之后,截至今天显然没有直接的解决方案,尽管他们说他们已经打开了一个功能请求,所以这个问题很快就会解决。
根据我的理解,需要此文件的原因是,当使用ProGuard进行模糊处理的生产版本发生异常时,New Relic系统可以显示未混淆的错误日志。
New Relic系统在此文件的帮助下,将ProGuard mapping.txt文件上传到New Relic服务器,并根据指定的令牌将其与您的应用程序相关联。通过这种方式,New Relic可以对堆栈跟踪进行取消混淆,并使用实际类和&amp ;;显示描述性堆栈跟踪。方法名称,而不是a,b,c等。
作为一种解决方法,我被告知如果我手动上传映射文件,我可以放弃这个文件。
可以在以下位置找到映射文件:
build/outputs/proguard/release/mapping.txt
要手动上传文件,请通过命令行执行以下操作:
curl -v -F proguard=@"<path_to_mapping.txt>" -H "X-APP-LICENSE-KEY:<APPLICATION_TOKEN>" https://mobile-symbol-upload.newrelic.com/symbol
必须对每个使用ProGuard进行混淆的变体(通常是发布版本)进行此操作。
希望这有助于其他人。
答案 1 :(得分:1)
我解决了创建一些Gradle任务的问题。请看一下https://discuss.newrelic.com/t/how-to-set-up-newrelic-properties-file-for-project-with-multiple-build-variants/46176/5
我遵循的代码对我来说非常有效。
在字符串资源文件上添加New Relic应用程序令牌。即:api.xml。
创建一个新的Gradle文件。即:newrelic-util.gradle。
在新创建的Gradle文件上添加以下内容:
apply plugin: 'com.android.application'
android.applicationVariants.all { variant ->
//<editor-fold desc="Setup New Relic property file">
def variantName = variant.name.capitalize()
def newRelicTasksGroup = "newrelic"
def projectDirPath = project.getProjectDir().absolutePath
def newRelicPropertyFileName = "newrelic.properties"
def newRelicPropertyFilePath = "${projectDirPath}/${newRelicPropertyFileName}"
// Cleanup task for New Relic property file creation process.
def deleteNewRelicPropertyFile = "deleteNewRelicPropertyFile"
def taskDeleteNewRelicPropertyFile = project.tasks.findByName(deleteNewRelicPropertyFile)
if (!taskDeleteNewRelicPropertyFile) {
taskDeleteNewRelicPropertyFile = tasks.create(name: deleteNewRelicPropertyFile) {
group = newRelicTasksGroup
description = "Delete the newrelic.properties file on project dir."
doLast {
new File("${newRelicPropertyFilePath}").with {
if (exists()) {
logger.lifecycle("Deleting file ${absolutePath}.")
delete()
} else {
logger.lifecycle("Nothing to do. File ${absolutePath} not found.")
}
}
}
}
}
/*
* Fix for warning message reported by task "newRelicMapUploadVariantName"
* Message:
* [newrelic] newrelic.properties was not found! Mapping file for variant [variantName] not uploaded.
* New Relic discussion:
* https://discuss.newrelic.com/t/how-to-set-up-newrelic-properties-file-for-project-with-multiple-build-variants/46176
*/
def requiredTaskName = "assemble${variantName}"
def taskAssembleByVariant = project.tasks.findByName(requiredTaskName)
def createNewRelicPropertyFileVariantName = "createNewRelicPropertyFile${variantName}"
// 0. Searching task candidate to be dependent.
if (taskAssembleByVariant) {
logger.debug("Candidate task to be dependent found: ${taskAssembleByVariant.name}")
// 1. Task creation
def taskCreateNewRelicPropertyFile = tasks.create(name: createNewRelicPropertyFileVariantName) {
group = newRelicTasksGroup
description = "Generate the newrelic.properties file on project dir.\nA key/value propety " +
"will be written in file to be consumed by newRelicMapUploadVariantName task."
logger.debug("Creating task: ${name}")
doLast {
def newRelicPropertyKey = "com.newrelic.application_token"
def newRelicStringResourceKey = "new_relic_key"
def targetResourceFileName = "api.xml"
def variantXmlResourceFilePath = "${projectDirPath}/src/${variant.name}/res/values/${targetResourceFileName}"
def mainXmlResourceFilePath = "${projectDirPath}/src/main/res/values/${targetResourceFileName}"
def xmlResourceFilesPaths = [variantXmlResourceFilePath, mainXmlResourceFilePath]
xmlResourceFilesPaths.any { xmlResourceFilePath ->
// 1.1. Searching xml resource file.
def xmlResourceFile = new File(xmlResourceFilePath)
if (xmlResourceFile.exists()) {
logger.lifecycle("Reading property from xml resource file: ${xmlResourceFilePath}.")
// 1.2. Searching for string name new_relic_key api.xml resource file.
def nodeResources = new XmlParser().parse(xmlResourceFile)
def nodeString = nodeResources.find {
Node nodeString -> nodeString.'@name'.toString() == newRelicStringResourceKey
}
// 1.3. Checking if string name new_relic_key was found.
if (nodeString != null) {
def newRelicApplicationToken = "${nodeString.value()[0]}"
logger.lifecycle("name:${nodeString.'@name'.toString()};" +
"value:${newRelicApplicationToken}")
// 1.4 Checking the content of newRelicApplicationToken
if (newRelicApplicationToken == 'null' || newRelicApplicationToken.allWhitespace) {
logger.warn("Invalid value for key ${newRelicStringResourceKey}. " +
"Please, consider configuring a value for key ${newRelicStringResourceKey}" +
" on ${xmlResourceFile.name} resource file for buildVariant: ${variantName}. " +
"The ${newRelicPropertyFileName} will be not created.")
return true // break the loop
}
// 1.5. File creation.
File fileProperties = new File(newRelicPropertyFilePath)
fileProperties.createNewFile()
logger.lifecycle("File ${fileProperties.absolutePath} created.")
// 1.6. Writing content on properties file.
def fileComments = "File generated dynamically by gradle task ${createNewRelicPropertyFileVariantName}.\n" +
"Don't change it manually.\n" +
"Don't track it on VCS."
new Properties().with {
load(fileProperties.newDataInputStream())
setProperty(newRelicPropertyKey, newRelicApplicationToken.toString())
store(fileProperties.newWriter(), fileComments)
}
logger.lifecycle("Properties saved on file ${fileProperties.absolutePath}.")
return true // break the loop
} else {
logger.warn("The key ${newRelicStringResourceKey} was not found on ${xmlResourceFile.absolutePath}.\n" +
"Please, consider configuring a key/value on ${xmlResourceFile.name} resource file for buildVariant: ${variantName}.")
return // continue to next xmlResourceFilePath
}
} else {
logger.error("Resource file not found: ${xmlResourceFile.absolutePath}")
return // continue next xmlResourceFilePath
}
}
}
}
// 2. Task dependency setup
// To check the task dependencies, use:
// logger.lifecycle("Task ${name} now depends on tasks:")
// dependsOn.forEach { dep -> logger.lifecycle("\tTask: ${dep}") }
tasks['clean'].dependsOn taskDeleteNewRelicPropertyFile
taskCreateNewRelicPropertyFile.dependsOn taskDeleteNewRelicPropertyFile
taskAssembleByVariant.dependsOn taskCreateNewRelicPropertyFile
} else {
logger.error("Required task ${requiredTaskName} was not found. " +
"The task ${createNewRelicPropertyFileVariantName} will be not created.")
}
//</editor-fold>
}
在app / build.gradle文件上,应用Gradle文件。
从以下位置应用:“ ./ newrelic-util.gradle”
就是这样。我在项目应用程序目录上创建了一个名为newrelic-util.gradle的文件。如果执行任务assembleAnyVariantName,将首先执行任务createNewRelicPropertyFileAnyVarianteName。提示:不要跟踪生成的文件newrelic.properties文件。在您的VCS上忽略它。
此外,任务deleteNewRelicPropertyFile将直接在任务“ clean”和“ createNewRelicPropertyFileAnyVarianteName”之前执行,以避免文件使用错误的New Relic应用程序令牌。