从 sbt 插件记录 [英] Logging from an sbt plugin

查看:16
本文介绍了从 sbt 插件记录的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我为 sbt 使用 s3 解析器插件.

I use the s3 resolver plugin for sbt.

我已更改凭据提供程序:

I have changed the credentials provider:

lazy val s3CredentialsProvider = {bucket: String =>
    new AWSCredentialsProviderChain(
      new EnvironmentVariableCredentialsProvider(),
      PropertyFilesCredentialProvider.create(bucket)
    )
  }

其中 PropertyFilesCredentialProvider 是自定义提供程序.

where the PropertyFilesCredentialProvider is a custom provider.

我在哪里做了以下事情:

I have done the following where:

  • 我们使用 sbt.util.internal

我们添加了一个System.out.println

改为.

我已经发布了插件,并一直在另一个插件中使用它.看起来我的插件正在被使用,因为解析器每次都尝试使用不同的访问密钥,但我们看不到我们班级的日志.

I've published the plugin and have been using it in another plugin. It looks like my plugin is being used because the resolver attempts to use a different access key each time but we cannot see the logs from our class.

当我们在 AutoPlugin 中有代码时,如何添加 logging?

How can we add logging when we have code in an AutoPlugin?

Provider 的代码如下所示:

The code for the Provider looks like this:

导入 java.io.{File, FileInputStream, InputStream}导入 java.util.Properties

import java.io.{File, FileInputStream, InputStream} import java.util.Properties

import com.amazonaws.auth.{AWSCredentials, AWSCredentialsProvider, BasicSessionCredentials}
import com.amazonaws.services.securitytoken.AWSSecurityTokenServiceClientBuilder
import com.amazonaws.services.securitytoken.model.{AssumeRoleRequest, AssumeRoleResult, Credentials}

/** Creates a credential provider that reads a `roleArn` property from a file
  * and assumes the role using STS.
  *
  * This is based on https://github.com/frugalmechanic/fm-sbt-s3-resolver/blob/master/src/main/scala/fm/sbt/S3URLHandler.scala#L84
  *
  * @param file Properties file holding the ROLE_ARN for the project.
  */
class PropertyFilesCredentialProvider(file: File)
  extends AWSCredentialsProvider {

  private val ROLE_ARN_KEY: String = "roleArn"
  private val AWS_REGION: String = "<AWS_REGION>"

  protected def getRoleArn: String = {
    val is: InputStream = new FileInputStream(file)
    try {
      val props: Properties = new Properties()
      props.load(is)
      props.getProperty(ROLE_ARN_KEY)
    } finally is.close()
  }

  def createAWSCredentials(credentials: Credentials): AWSCredentials = {
    System.out.println("Retrieved AWS Session Token and Credentials for assuming role")
    new BasicSessionCredentials(credentials.getAccessKeyId,
      credentials.getSecretAccessKey,
      credentials.getSessionToken)
  }

  def assumeRole(roleArn: String): AssumeRoleResult = {
    System.out.println(s"Making a request to AWS STS with the roleArn: $roleArn to assume a role")
    val stsClient = AWSSecurityTokenServiceClientBuilder
      .standard
      .withRegion(AWS_REGION)
      .build

    val assumeRoleRequest = new AssumeRoleRequest
    assumeRoleRequest.setRoleArn(roleArn)
    stsClient.assumeRole(assumeRoleRequest)
  }

  override def getCredentials: AWSCredentials = {
    val roleArn = getRoleArn
    if (roleArn == null || roleArn == "") {
      System.out.println(s"Key of name $ROLE_ARN_KEY was not found in file at ${file.getAbsolutePath}")
      return null
    }
    System.out.println(s"$ROLE_ARN_KEY was read from ${file.getAbsolutePath} successfully")
    val assumeRoleResult = assumeRole(roleArn)
    System.out.println("Request to assume role using AWS STS successful")
    createAWSCredentials(assumeRoleResult.getCredentials)
  }

  override def refresh(): Unit = {}
}

object PropertyFilesCredentialProvider {

  private val DOT_SBT_DIR: File =
    new File(System.getProperty("user.home"), ".sbt")

  /** Uses a bucket specific propertyfile to read AWS `roleArn` from and provides it
    * to the PropertyFilesCredentialProvider.
    *
    * @param bucket Name of the S3 bucket.
    * @return a PropertyFileCredentialProvider
    */
  def create(bucket: String): PropertyFilesCredentialProvider = {
    val fileName = s".${bucket}_s3credentials"
    System.out.println("Using the Property Files Credential Provider")
    System.out.println(s"Reading $fileName for AWS Credentials ")
    val file: File = new File(DOT_SBT_DIR, fileName)
    new PropertyFilesCredentialProvider(file)
  }
}

更新

尝试使用 streams.value.log 失败并出现以下错误:

Attempts to use streams.value.log failed with the error:

`value` can only be called on a task within a task definition macro, 
 such as :=, +=, ++=, or Def.task.
[error]   val logger = streams.value.log
[error]   

尝试使用 ConsoleLogger,它是 Logger 的子类,但在类中实例化.它上面的 apply 方法是这样调用的:

Attempts to use ConsoleLogger which is a sub class of Logger but instantiated within the class. The apply method on it was invoked like this:

val logger = sbt.internal.util.ConsoleLogger(System.out)
logger.info("s"Key of name $ROLE_ARN_KEY was not found in file at ${file.getAbsolutePath}"")

在上面的类中.那也没有输出日志.

inside the class above. That did not output the logs either.

我创建了两个记录器,一个用于类,另一个用于作为成员的伴生对象,而不是在扩展 AutoPlugin 的类中.

I created two loggers, one for the class and another for the companion object as members and not within the class extending AutoPlugin.

推荐答案

这是一种将日志记录添加到您为 AutoPlugin 定义的类的方法

This is a way of adding logging to classes that you defined for your AutoPlugin

import sbt._
import sbt.Keys._

object TestPlugin extends AutoPlugin {

  class SomeClassThatNeedsLogger(logger: Logger) {
    def doSomeLogging(): Unit = {
      logger.info("It logs")
    }
  }

  object autoImport {
    val someClassThatNeedsLoggerHolder = taskKey[SomeClassThatNeedsLogger]("Holds instance of SomeClassThatNeedsLogger")
    val runSomeClassThatNeedsLogger = taskKey[Unit]("Runs SomeClassThatNeedsLogger")
  }

  import autoImport._

  override def trigger = allRequirements

  override def projectSettings: Seq[Def.Setting[_]] = {
    Seq(
      someClassThatNeedsLoggerHolder := new SomeClassThatNeedsLogger(streams.value.log),
      runSomeClassThatNeedsLogger := someClassThatNeedsLoggerHolder.value.doSomeLogging()
    )
  }
}

运行给了我们一个日志条目:

And running gives us a log entry:

> runSomeClassThatNeedsLogger
[info] It logs
[success] Total time: 0 s, completed Feb 6, 2019 9:47:15 AM

关于您遇到的错误的一些说明

Some notes on errors you're getting

value 只能在任务定义宏中的任务上调用,
例如 :=、+=、++= 或 Def.task.

value can only be called on a task within a task definition macro,
such as :=, +=, ++=, or Def.task.

这告诉您 streams.value 只能在任务定义中使用,例如 someClassThatNeedsLoggerHolder := new SomeClassThatNeedsLogger(streams.value.log)

This tells you that streams.value can be used only in task definitions like someClassThatNeedsLoggerHolder := new SomeClassThatNeedsLogger(streams.value.log)

ConsoleLogger 使用 sbt.util.internal

ConsoleLogger using sbt.util.internal

正如包名所暗示的,这是一个内部包,在 sbt 的下一个版本中会发生变化.它不应该在你的插件定义中使用.

As the package name suggests, this is an internal package and subject to a change in next versions of sbt. It should not be used in your plugin definitions.

这篇关于从 sbt 插件记录的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆