L'agent Java de New Relic est compatible avec Scala et prend encharge l'appel d'API New Relic, les annotations et instrumentation personnalisée . Toutes les informations ci-dessous complètent les instructions d'installation de l'agent Java de New Relic.
Conseil
Pour Heroku, voir agent Java avec Scala sur Heroku.
Cadre Scala
Si votre framework n'est pas supporté nativement par New Relic, ou si vous souhaitez mettre en place monitoring supplémentaire, instrumentation personnalisée est un excellent moyen d'approfondir votre application.
Instrumenter Scala avec l'Scala API
L'API de l'agent New Relic Scala vous permet de contrôler, de personnaliser et d'étendre les fonctionnalités de l'agent Java APM à l'aide de code Scala idiomatique. Il peut être utilisé avec l'et permettre à l'utilisateur New Relic Java API de
- Créer des segments pour les fonctions anonymes synchrones et asynchrones
- Créer des segments pour les blocs de code synchrones et asynchrones
- Créer une transaction si aucune n'a déjà été démarrée
Important
Pour de meilleurs résultats lors de l'utilisation de l'API, assurez-vous que vous disposez de la dernière version agent Java . L'API Scala de New Relic nécessite l'agent Java 7.1.0 ou supérieur.
Utiliser l'API Scala
Pour accéder à la classe API, ajoutez les informations suivantes à votre fichier de configuration Scala :
Les versions Scala prises en charge sont 2.10, 2.11, 2.12 et 2.13. Scala 3.0 utilisateur peut utiliser le pot 2.13. Le fichier jar est déployé sur Maven Central et se trouve également dans le fichier zip d'installation de l'agent Java de New Relic. Vous pouvez appeler l'API lorsque l'agent Java n'est pas en cours d'exécution. Les méthodes API sous-jacentes ne sont que des stubs ; l'implémentation est ajoutée lorsque l'agent Java est en cours d'exécution.
Segments
Pour créer un segment pour un bloc de code synchrone, utilisez TraceOps.trace. Par exemple:
import com.newrelic.scala.api.TraceOps.trace
trace("statement segment") { val i = 1 val j = 2 println(i + j)}// trace can also be used as an expressionval x: Int = trace("expression segment") { val i = 1 val j = 2 i + j}println(x) // 2trace peut également être utilisé en Scala pour les compréhensions
import scala.concurrent.{ExecutionContext, Future}import com.newrelic.scala.api.TraceOps.trace
// implicit executionimplicit val ec: ExecutionContext = ???
val x: Option[Int] = for { one <- trace("segment one")(Option(1)) two <- trace("segment two")(Option(one + 1)) three <- trace("segment three")(Option(two + 1)) } yield three
println(x) // Some(3)Si vous souhaitez créer un segment pour un bloc de code asynchrone contenant un Scala Future, utilisez TraceOps.asyncTrace. Cela garantira que le timing du segment inclut le temps nécessaire à l'achèvement du futur.
Dans l'exemple ci-dessous, le temps de segment ne sera pas inférieur à 5 secondes en raison du retard créé dans le futur enveloppé.
import scala.concurrent.{ExecutionContext, Future}import com.newrelic.scala.api.TraceOps.asyncTrace// implicit executionimplicit val ec: ExecutionContext = ???
val x: Future[Int] = asyncTrace("segment name")(Future { Thread.sleep(5000) 1})x.foreach(println) // prints 1 on completion of FutureasyncTrace peut également être utilisé en Scala pour les compréhensions
import scala.concurrent.{ExecutionContext, Future}import com.newrelic.scala.api.TraceOps.asyncTrace// implicit executionimplicit val ec: ExecutionContext = ???
val x: Future[Int] = for { one <- asyncTrace("segment one")(Future(1)) two <- asyncTrace("segment two")(Future(one + 1)) three <- asyncTrace("segment three")(Future(two + 1)) } yield three
x.foreach(println) // prints 3 on completion of FutureSi vous souhaitez créer un segment pour une fonction anonyme synchrone, utilisez TraceOps.traceFun. Par exemple:
import com.newrelic.scala.api.TraceOps.traceFun
val x: Option[Int] = Option(1) .map(traceFun("statement segment")(i => i + 1))println(x) //Some(2)Si vous souhaitez créer un segment pour une fonction asynchrone qui renvoie un Scala Future, utilisez TraceOps.asyncTraceFun. Cela garantira que le timing du segment inclut le temps nécessaire à l'exécution de la fonction.
Dans l'exemple ci-dessous, le temps de segment ne sera pas inférieur à 5 secondes en raison du retard créé dans le futur enveloppé.
import scala.concurrent.{ExecutionContext, Future}import com.newrelic.scala.api.TraceOps.asyncTraceFun// implicit executionimplicit val ec: ExecutionContext = ???
val x: Future[Int] = Future(1) .flatMap(asyncTraceFun("statement segment")(i => Future(i + 1)))
x.foreach(println) // prints 2 on completion of FutureTransactions
Les transactions peuvent être créées à l'aide de la méthode TraceOps.txn . Par exemple:
import com.newrelic.scala.api.TraceOps.txntxn { val i = 1 val j = 2 println(i + j)}txn peut être utilisé comme une déclaration (comme ci-dessus) ou comme une expression
import com.newrelic.scala.api.TraceOps.txnval i: Int = txn(1 + 2) //transaction createdprintln(i) // 3txn peut être utilisé avec l'une des méthodes TraceOp pour créer des segments. L'exemple ci-dessous crée une transaction avec 3 segments.
import scala.concurrent.{ExecutionContext, Future}import com.newrelic.scala.api.TraceOps.{txn, asyncTrace}// implicit executionimplicit val ec: ExecutionContext = ???
val x: Future[Int] = txn( for { one <- asyncTrace("segment one")(Future(1)) two <- asyncTrace("segment two")(Future(one + 1)) three <- asyncTrace("segment three")(Future(two + 1)) } yield three )
x.foreach(println) // prints 3 on completion of FutureLes contrats à terme
New Relic gère les futurs Scala lorsqu'ils sont soumis à l'exécuteur, ce qui peut conduire à un résultat différent de celui attendu. Les exemples de cette section expliquent comment cela fonctionne, afin que vous sachiez à quoi vous attendre.
Cet exemple appelle un bloc de transaction, qui prend un futur comme paramètre et le chaîne à un deuxième futur :
def chainFutures(f: Future[Unit]) = txn { f.flatMap( _ => Future{ Thread.sleep(2000) })}
val f = Future { Thread.sleep(5000) } // future is submitted herechainFutures(f)À partir de cet exemple, vous pourriez vous attendre à ce que chainFutures produise une transaction d’une durée de 7 secondes. Au lieu de cela, l’UI de New Relic indique que la transaction se termine immédiatement.
Le futur f dans l'exemple est soumis par la méthode Future.apply() lors de sa construction, avant que la transaction n'existe. Étant donné que f est soumis en dehors de la transaction, tous les threadhops effectués pendant son exécution ne sont pas liés entre eux. Le futur en aval, Future{ Thread.sleep(2000) }, n'est pas non plus suivi, car il est soumis dans un rappel après que le contexte de transaction a déjà disparu.
Il y a quelques choses que vous pouvez faire pour que votre transaction capture le timing des deux contrats à terme.
- Même si vous ne pouvez pas modifier le moment où
fest soumis, vous pouvez le chronométrer à l'aide de la méthodeasyncTrace. Cette fois,fs'écoule à partir du moment où la transaction est démarrée (mais ne suivra pas les threadhops effectués parf). - Vous pouvez utiliser un jeton pour lier le rappel à la transaction afin de préserver le contexte de la transaction pendant les threadhops.
- Vous pouvez ajouter un autre
asyncTracepour chronométrer le deuxième futur.
Avec cet exemple, l'UI de New Relic montre une transaction d'une durée d'environ 7 secondes, avec deux segments nommés « premier futur » et « deuxième futur » :
def chainFutures(f: Future[Unit]) = txn { val t = NewRelic.getAgent.getTransaction.getToken asyncTrace("future one" )(f) //asyncTrace times the first future .flatMap(_ => { t.linkAndExpire() //token links across potential threadhops asyncTrace("future two"){ Future { Thread.sleep(2000) } } //asyncTrace times the second future })}val f = Future { Thread.sleep(5000) } chainFutures(f)Instrumenter Scala avec l'agent Java API
Instrumenter Scala pour utiliser la classe API New Relic ou les annotations.
Ajoutez les informations suivantes à votre fichier de configuration Scala :
Importez la classe NewRelic et utilisez-la dans votre application :
import com.newrelic.api.agent.NewRelic...NewRelic.setTransactionName(null, "/myTransaction");
Plus de fonctions API
Pour en savoir plus sur l'API de l'agent Java et ses fonctionnalités, consultez l'introduction à l'API de l'agent Java.
Instrumenter Scala avec instrumentation XML
L'instrumentation XML est disponible pour toute application Scala exécutée avec l'agent Java. Il permet d'ajouter de l'instrumentation sans aucune modification du code.
Pour plus d'informations, voir Instrumentation Java par XML.
instrumentationsupplémentaire
Si vous utilisez Kamon, jetez un œil au reporter New Relic Kamon.