split lila.mon from lila.common

to parallelize compilation
This commit is contained in:
Thibault Duplessis
2026-05-05 11:25:21 +02:00
parent 6b0d61139d
commit 896c90c344
124 changed files with 1169 additions and 1067 deletions
+1 -1
View File
@@ -81,7 +81,7 @@ final class LilaComponents(
val env: lila.app.Env =
lila.log("boot").info(s"Start loading lila modules")
val c = lila.common.Chronometer.sync(wire[lila.app.Env])
val c = lila.mon.Chronometer.sync(wire[lila.app.Env])
lila.log("boot").info(s"Loaded lila modules in ${c.showDuration}")
c.result
+11 -8
View File
@@ -1,11 +1,11 @@
package controllers
import scala.annotation.nowarn
import alleycats.Zero
import play.api.libs.json.Json
import play.api.mvc.*
import scala.annotation.nowarn
import lila.app.{ *, given }
import lila.common.HTTPRequest
import lila.core.id.ImageId
@@ -15,6 +15,7 @@ import lila.core.security.FingerHash
import lila.core.userId.ModId
import lila.mod.{ Modlog, ModUserSearch }
import lila.report.{ Mod as AsMod, Suspect }
import lila.mon.extensions.*
final class Mod(
env: Env,
@@ -253,26 +254,28 @@ final class Mod(
given lila.mod.IpRender.RenderIp = env.mod.ipRender.apply
env.game.gameRepo
.recentPovsByUserFromSecondary(user, 80)
.mon(_.mod.comm.segment("recentPovs"))
.mon(lila.mon.mod.comm.segment("recentPovs"))
.flatMap: povs =>
(
env.api.modTimeline.load(user, withPlayBans = false).mon(_.mod.comm.segment("modTimeline")),
env.api.modTimeline
.load(user, withPlayBans = false)
.mon(lila.mon.mod.comm.segment("modTimeline")),
priv.so:
env.chat.api.playerChat
.optionsByOrderedIds(povs.map(_.gameId.into(ChatId)))
.mon(_.mod.comm.segment("playerChats"))
.mon(lila.mon.mod.comm.segment("playerChats"))
,
priv.so:
env.msg.api
.recentByForMod(user, 30)
.mon(_.mod.comm.segment("pms"))
.mon(lila.mon.mod.comm.segment("pms"))
,
env.shutup.api
.getPublicLines(user.id)
.mon(_.mod.comm.segment("publicChats")),
.mon(lila.mon.mod.comm.segment("publicChats")),
env.report.api.inquiries
.ofModId(me.id)
.mon(_.mod.comm.segment("inquiries")),
.mon(lila.mon.mod.comm.segment("inquiries")),
env.security.userLogins(user, 100).flatMap {
userC.loginsTableData(user, _, 100)
}
+3 -2
View File
@@ -18,8 +18,8 @@ import lila.study.PgnDump.WithFlags
import lila.study.Study.WithChapter
import lila.study.{ Who, Chapter, Orders, Settings, Study as StudyModel, StudyForm }
import lila.tree.Node.partitionTreeWriter
import com.fasterxml.jackson.core.JsonParseException
import lila.ui.Page
import lila.mon.extensions.*
final class Study(
env: Env,
@@ -258,7 +258,7 @@ final class Study(
}.optionFu:
env.chat.api.userChat
.findMine(study.id.into(ChatId))
.mon(_.chat.fetch("study"))
.mon(lila.mon.chat.fetch("study"))
def createAs = AuthBody { ctx ?=> me ?=>
bindForm(StudyForm.importGame.form)(
@@ -567,6 +567,7 @@ final class Study(
bindForm(StudyForm.topicsForm)(
_ => Redirect(routes.Study.topics),
topics =>
import com.fasterxml.jackson.core.JsonParseException
try env.study.topicApi.userTopics(me, topics).inject(Redirect(routes.Study.topics))
catch case e: JsonParseException => BadRequest(e.getMessage)
)
+3 -2
View File
@@ -2,13 +2,14 @@ package controllers
import play.api.libs.json.*
import play.api.mvc.*
import scalalib.data.Preload
import lila.app.{ *, given }
import lila.common.HTTPRequest
import lila.common.Json.given
import scalalib.data.Preload
import lila.gathering.Condition.GetMyTeamIds
import lila.tournament.{ MyInfo, Tournament as Tour, TournamentForm }
import lila.mon.extensions.*
final class Tournament(env: Env, apiC: => Api)(using akka.stream.Materializer) extends LilaController(env):
@@ -119,7 +120,7 @@ final class Tournament(env: Env, apiC: => Api)(using akka.stream.Materializer) e
yield Ok(json.add("chat" -> jsChat)).noCache
)
.monSuccess:
_.tournament.apiShowPartial(partial = getBool("partial"), HTTPRequest.clientName(ctx.req))
lila.mon.tournament.apiShowPartial(partial = getBool("partial"), HTTPRequest.clientName(ctx.req))
def apiShow(id: TourId) = AnonOrScoped(): ctx ?=>
WithVisibleTournament(id): tour =>
+3 -2
View File
@@ -18,6 +18,7 @@ import lila.rating.PerfType
import lila.rating.UserPerfsExt.best8Perfs
import lila.security.UserLogins
import lila.user.WithPerfsAndEmails
import lila.mon.extensions.*
final class User(
override val env: Env,
@@ -88,7 +89,7 @@ final class User(
_ <- env.userInfo.preloadTeams(info)
social <- env.socialInfo(u)
page <- renderPage:
lila.mon.chronoSync(_.user.segment("renderSync")):
lila.mon.chronoSync(lila.mon.user.segment("renderSync")):
views.user.show.page.activity(as, info, social)
yield status(page).withCanonical(routes.User.show(u.username))
else
@@ -304,7 +305,7 @@ final class User(
private def modZoneSegment(fu: Fu[Frag], name: String, user: UserModel): Source[Frag, ?] =
Source.futureSource:
fu.monSuccess(_.mod.zoneSegment(name))
fu.monSuccess(lila.mon.mod.zoneSegment(name))
.logFailure(lila.log("modZoneSegment").branch(s"$name ${user.id}"))
.map(Source.single)
+4 -2
View File
@@ -2,8 +2,10 @@ package lila.app
package http
import play.api.mvc.*
import lila.app.{ *, given }
import lila.memo.CacheApi.*
import lila.mon.extensions.*
final class KeyPages(val env: Env)(using Executor)
extends lila.web.ResponseWriter
@@ -28,12 +30,12 @@ final class KeyPages(val env: Env)(using Executor)
simuls = env.simul.allCreatedFeaturable.get {}.recoverDefault,
streamerSpots = env.streamer.homepageMaxSetting.get()
)
.mon(_.lobby.segment("preloader.total"))
.mon(lila.mon.lobby.segment("preloader.total"))
.flatMap: h =>
ctx.me.filter(_.hasTitle).foreach(env.msg.systemMsg.twoFactorReminder(_))
ctx.me.filterNot(_.hasEmail).foreach(env.msg.systemMsg.emailReminder(_))
renderPage:
lila.mon.chronoSync(_.lobby.segment("renderSync")):
lila.mon.chronoSync(lila.mon.lobby.segment("renderSync")):
views.lobby.home(h)
def notFound(msg: Option[String])(using Context): Fu[Result] =
+12 -11
View File
@@ -14,6 +14,7 @@ import lila.timeline.Entry
import lila.tournament.Tournament
import lila.ublog.UblogPost
import lila.user.{ LightUserApi, Me, User }
import lila.mon.extensions.*
final class Preload(
tv: lila.tv.Tv,
@@ -63,19 +64,19 @@ final class Preload(
),
lichessMsg
) <- lobbyApi.get
.mon(_.lobby.segment("lobbyApi"))
.zip(tours.mon(_.lobby.segment("tours")))
.zip(events.mon(_.lobby.segment("events")))
.zip(simuls.mon(_.lobby.segment("simuls")))
.zip(tv.getBestGame.mon(_.lobby.segment("tvBestGame")))
.zip((ctx.userId.so(timelineApi.userEntries)).mon(_.lobby.segment("timeline")))
.zip((ctx.noBot.so(dailyPuzzle())).mon(_.lobby.segment("puzzle")))
.mon(lila.mon.lobby.segment("lobbyApi"))
.zip(tours.mon(lila.mon.lobby.segment("tours")))
.zip(events.mon(lila.mon.lobby.segment("events")))
.zip(simuls.mon(lila.mon.lobby.segment("simuls")))
.zip(tv.getBestGame.mon(lila.mon.lobby.segment("tvBestGame")))
.zip((ctx.userId.so(timelineApi.userEntries)).mon(lila.mon.lobby.segment("timeline")))
.zip((ctx.noBot.so(dailyPuzzle())).mon(lila.mon.lobby.segment("puzzle")))
.zip:
ctx.kid.no.so:
liveStreamApi.all
.dmap(_.homepage(streamerSpots, ctx.acceptLanguages).withTitles(lightUserApi))
.mon(_.lobby.segment("streams"))
.zip((ctx.userId.so(playbanApi.currentBan)).mon(_.lobby.segment("playban")))
.mon(lila.mon.lobby.segment("streams"))
.zip((ctx.userId.so(playbanApi.currentBan)).mon(lila.mon.lobby.segment("playban")))
.zip(ctx.blind.so(ctx.me).so(roundProxy.urgentGames))
.zip(ublogApi.myCarousel)
.zip:
@@ -85,11 +86,11 @@ final class Preload(
.so(unreadCount.hasLichessMsg)
(currentGame, _) <- ctx.me
.soUse(currentGameMyTurn(povs, lightUserApi.sync))
.mon(_.lobby.segment("currentGame"))
.mon(lila.mon.lobby.segment("currentGame"))
.zip:
lightUserApi
.preloadMany(entries.flatMap(_.userIds).toList)
.mon(_.lobby.segment("lightUsers"))
.mon(lila.mon.lobby.segment("lightUsers"))
classes <- ctx.myId.so(me => clasApi.isStudent(me).so(clasApi.clas.ofStudent(me, 4)))
yield Homepage(
data,
+23 -20
View File
@@ -7,12 +7,13 @@ import lila.bookmark.BookmarkApi
import lila.core.data.SafeJsonStr
import lila.core.perf.UserWithPerfs
import lila.core.user.User
import lila.core.security.IsProxy
import lila.core.perm.Granter
import lila.forum.ForumPostApi
import lila.game.Crosstable
import lila.relation.RelationApi
import lila.ublog.{ UblogApi, UblogPost }
import lila.core.security.IsProxy
import lila.core.perm.Granter
import lila.mon.extensions.*
case class UserInfo(
nbs: UserInfo.NbGames,
@@ -56,10 +57,10 @@ object UserInfo:
def apply(u: User)(using ctx: Context): Fu[Social] =
given scala.concurrent.ExecutionContext = scala.concurrent.ExecutionContext.parasitic
(
ctx.userId.so(relationApi.fetchRelation(_, u.id).mon(_.user.segment("relation"))),
ctx.useMe(noteApi.getForMyPermissions(u).mon(_.user.segment("notes"))),
ctx.isAuth.so(prefApi.followable(u.id).mon(_.user.segment("followable"))),
ctx.userId.so(myId => relationApi.fetchBlocks(u.id, myId).mon(_.user.segment("blocks")))
ctx.userId.so(relationApi.fetchRelation(_, u.id).mon(lila.mon.user.segment("relation"))),
ctx.useMe(noteApi.getForMyPermissions(u).mon(lila.mon.user.segment("notes"))),
ctx.isAuth.so(prefApi.followable(u.id).mon(lila.mon.user.segment("followable"))),
ctx.userId.so(myId => relationApi.fetchBlocks(u.id, myId).mon(lila.mon.user.segment("blocks")))
).mapN(Social.apply)
case class NbGames(
@@ -81,11 +82,13 @@ object UserInfo:
withCrosstable.so:
me
.filter(u.isnt(_))
.traverse(me => crosstableApi.withMatchup(me.userId, u.id).mon(_.user.segment("crosstable")))
.traverse(me =>
crosstableApi.withMatchup(me.userId, u.id).mon(lila.mon.user.segment("crosstable"))
)
,
gameCached.nbPlaying(u.id).mon(_.user.segment("nbPlaying")),
gameCached.nbImportedBy(u.id).mon(_.user.segment("nbImported")),
bookmarkApi.countByUser(u).mon(_.user.segment("nbBookmarks"))
gameCached.nbPlaying(u.id).mon(lila.mon.user.segment("nbPlaying")),
gameCached.nbImportedBy(u.id).mon(lila.mon.user.segment("nbImported")),
bookmarkApi.countByUser(u).mon(lila.mon.user.segment("nbBookmarks"))
).mapN(NbGames.apply)
final class UserInfoApi(
@@ -112,19 +115,19 @@ object UserInfo:
def showRatings = ctx.noBlind && ctx.pref.showRatings && isAuthOrNotProxied
(
perfsRepo.withPerfs(user),
userApi.getTrophiesAndAwards(user).mon(_.user.segment("trophies")),
(nbs.playing > 0).so(simulApi.isSimulHost(user.id).mon(_.user.segment("simul"))),
showRatings.so(ratingChartApi(user)).mon(_.user.segment("ratingChart")),
userApi.getTrophiesAndAwards(user).mon(lila.mon.user.segment("trophies")),
(nbs.playing > 0).so(simulApi.isSimulHost(user.id).mon(lila.mon.user.segment("simul"))),
showRatings.so(ratingChartApi(user)).mon(lila.mon.user.segment("ratingChart")),
(!user.is(UserId.lichess) && !user.isBot).so:
postApi.nbByUser(user.id).mon(_.user.segment("nbForumPosts"))
postApi.nbByUser(user.id).mon(lila.mon.user.segment("nbForumPosts"))
,
withUblog.so(ublogApi.userBlogPreviewFor(user, 3)),
studyRepo.countByOwner(user.id).recoverDefault.mon(_.user.segment("nbStudies")),
simulApi.countHostedByUser.get(user.id).mon(_.user.segment("nbSimuls")),
relayApi.countOwnedByUser.get(user.id).mon(_.user.segment("nbBroadcasts")),
ctx.useMe(teamApi.joinedTeamIdsOfUserAsSeenBy(user).mon(_.user.segment("teamIds"))),
streamerApi.isActualStreamer(user).mon(_.user.segment("streamer")),
coachApi.isListedCoach(user).mon(_.user.segment("coach")),
studyRepo.countByOwner(user.id).recoverDefault.mon(lila.mon.user.segment("nbStudies")),
simulApi.countHostedByUser.get(user.id).mon(lila.mon.user.segment("nbSimuls")),
relayApi.countOwnedByUser.get(user.id).mon(lila.mon.user.segment("nbBroadcasts")),
ctx.useMe(teamApi.joinedTeamIdsOfUserAsSeenBy(user).mon(lila.mon.user.segment("teamIds"))),
streamerApi.isActualStreamer(user).mon(lila.mon.user.segment("streamer")),
coachApi.isListedCoach(user).mon(lila.mon.user.segment("coach")),
fideIdOf(user.light),
fuccess(Granter.opt(_.SeeInsight)) >>| (user.count.rated >= 50).so(insightShare.grant(user))
).mapN(UserInfo(nbs, _, _, _, _, _, _, _, _, _, _, _, _, _, _))
+1
View File
@@ -5,6 +5,7 @@ import scalalib.StringUtils.escapeHtmlRaw
import lila.app.UiEnv.{ *, given }
import lila.common.String.html.safeJsonValue
import lila.ui.{ RenderedPage, PageFlags }
import lila.mon.extensions.*
object page:
+11 -6
View File
@@ -98,6 +98,11 @@ lazy val coreI18n = module("coreI18n",
Seq(scalatags) ++ scalalib.bundle
)
lazy val mon = module("mon",
Seq(core),
Seq(kamon.core, kamon.influxdb)
)
lazy val common = module("common",
Seq(core),
Seq(
@@ -111,7 +116,7 @@ lazy val db = module("db",
)
lazy val memo = module("memo",
Seq(db),
Seq(db, mon),
Seq(scaffeine, bloomFilter) ++ playWs.bundle
)
@@ -130,7 +135,7 @@ lazy val i18n = module("i18n",
)
lazy val rating = module("rating",
Seq(db, ui),
Seq(db, ui, mon),
tests.bundle ++ Seq(apacheMath)
).dependsOn(common % "test->test")
@@ -346,7 +351,7 @@ lazy val security = module("security",
)
lazy val shutup = module("shutup",
Seq(db),
Seq(db, mon),
tests.bundle
)
@@ -401,17 +406,17 @@ lazy val playban = module("playban",
)
lazy val push = module("push",
Seq(db),
Seq(db, mon),
playWs.bundle ++ Seq(googleOAuth)
)
lazy val irc = module("irc",
Seq(common),
Seq(common, mon),
playWs.bundle
)
lazy val mailer = module("mailer",
Seq(memo, coreI18n, ui),
Seq(memo, ui),
Seq(hasher, play.mailer)
)
@@ -2,13 +2,14 @@ package lila.activity
import play.api.i18n.Lang
import scalalib.HeapSort
import chess.Speed.Correspondence
import lila.core.chess.Rank
import lila.core.game.LightPov
import lila.core.swiss.IdName as SwissIdName
import lila.db.AsyncCollFailingSilently
import lila.db.dsl.*
import chess.Speed.Correspondence
import lila.mon.extensions.*
final class ActivityReadApi(
coll: AsyncCollFailingSilently,
@@ -37,12 +38,12 @@ final class ActivityReadApi(
.cursor[Activity]()
.list(Activity.recentNb)
).dmap(_.filterNot(_.isEmpty))
.mon(_.user.segment("activity.raws"))
.mon(lila.mon.user.segment("activity.raws"))
practiceStudies <- activities
.exists(_.practice.isDefined)
.optionFu(getPracticeStudies())
views <- activities.sequentially: a =>
one(practiceStudies, a).mon(_.user.segment("activity.view"))
one(practiceStudies, a).mon(lila.mon.user.segment("activity.view"))
_ <- preloadAll(views)
yield addSignup(u.createdAt, views)
@@ -56,7 +57,7 @@ final class ActivityReadApi(
allForumPosts <- a.forumPosts.traverse: p =>
forumPostApi
.miniViews(p.value)
.mon(_.user.segment("activity.posts"))
.mon(lila.mon.user.segment("activity.posts"))
hiddenForumTeamIds <- teamApi.filterHideForum(
(~allForumPosts).flatMap(_.topic.possibleTeamId).distinct
)
@@ -67,7 +68,7 @@ final class ActivityReadApi(
.traverse: p =>
ublogApi
.liveLightsByIds(p.value)
.mon(_.user.segment("activity.ublogs"))
.mon(lila.mon.user.segment("activity.ublogs"))
.dmap(_.filter(_.nonEmpty))
practice =
for
@@ -117,7 +118,7 @@ final class ActivityReadApi(
)
)
)
.mon(_.user.segment("activity.tours"))
.mon(lila.mon.user.segment("activity.tours"))
swisses <-
a.swisses.so: swisses =>
toSwissesView(swisses.value).dmap(_.nonEmptyOption)
+2 -1
View File
@@ -6,6 +6,7 @@ import lila.common.Json.given
import lila.core.perf.{ UserPerfs, UserWithPerfs }
import lila.lobby.LobbySocket
import lila.rating.UserPerfsExt.perfsList
import lila.mon.extensions.*
final class LobbyApi(
lightUserApi: lila.user.LightUserApi,
@@ -16,7 +17,7 @@ final class LobbyApi(
def get(using me: Option[UserWithPerfs]): Fu[(JsObject, List[Pov])] =
me.so(gameProxyRepo.urgentGames)
.mon(_.lobby.segment("urgentGames"))
.mon(lila.mon.lobby.segment("urgentGames"))
.flatMap: povs =>
val displayedPovs = povs.take(9)
for _ <- lightUserApi.preloadMany(displayedPovs.flatMap(_.opponent.userId))
+4 -3
View File
@@ -19,6 +19,7 @@ import lila.swiss.GameView as SwissView
import lila.tournament.GameView as TourView
import lila.tree.{ ExportOptions, Tree }
import lila.game.GameExt.timeForFirstMove
import lila.mon.extensions.*
final private[api] class RoundApi(
jsonView: JsonView,
@@ -66,7 +67,7 @@ final private[api] class RoundApi(
.compose(withForecastCount(forecast.map(_.steps.size)))
.compose(withOpponentSignal(pov))
)(json)
}.mon(_.round.api.player)
}.mon(lila.mon.round.api.player)
def watcher(
pov: Pov,
@@ -94,7 +95,7 @@ final private[api] class RoundApi(
.compose(withBookmark(bookmarked))
.compose(withSteps(pov, initialFen))
)(json)
}.mon(_.round.api.watcher)
}.mon(lila.mon.round.api.watcher)
private def ctxFlags(using ctx: Context) =
ExportOptions(
@@ -144,7 +145,7 @@ final private[api] class RoundApi(
.compose(withPuzzleOpening(puzzleOpening))
)(json)
.flatMap(externalEngineApi.withExternalEngines)
.mon(_.round.api.watcher)
.mon(lila.mon.round.api.watcher)
def userAnalysisJson(
pov: Pov,
@@ -115,7 +115,7 @@ final class ChallengeApi(
maxSize = Max(64),
timeout = 5.seconds,
"challengeAccept",
lila.log.asyncActorMonitor.full
lila.mon.asyncActorMonitor.full
)
def accept(
@@ -35,7 +35,7 @@ final class ChallengeBulkApi(
expiration = 10.minutes,
timeout = 10.seconds,
name = "challenge.bulk",
lila.log.asyncActorMonitor.full
lila.mon.asyncActorMonitor.full
)
def scheduledBy(me: User): Fu[List[ScheduledBulk]] =
+1
View File
@@ -6,6 +6,7 @@ import play.api.i18n.Lang
import lila.common.Form.{ cleanNonEmptyText, cleanText, into }
import lila.clas.Student.RealName
import lila.mon.extensions.*
final class ClasForm(
lightUserAsync: lila.core.LightUser.Getter,
+2 -1
View File
@@ -8,6 +8,7 @@ import reactivemongo.api.bson.BSONNull
import play.api.Mode
import lila.db.dsl.{ *, given }
import lila.mon.extensions.*
final class ClasUserFilters(using Executor, Materializer, Scheduler)(colls: ClasColls)(using mode: Mode):
@@ -84,7 +85,7 @@ private final class ClasUserCache(name: String)(
lila.mon.clas.bloomFilter(name).count.update(nb)
bloomFilter.dispose()
bloomFilter = nextBloom
.monSuccess(_.clas.bloomFilter(name).fu)
.monSuccess(lila.mon.clas.bloomFilter(name).fu)
scheduler.scheduleWithFixedDelay(initialDelay, 7.days): () =>
rebuildBloomFilter()
+6 -2
View File
@@ -1,8 +1,12 @@
package lila.common
import scalalib.actor.{ AsyncActorBounded, AsyncActorSequencer }
// provides values of A one by one
// but generates them in batches
final class BatchProvider[A](name: String, timeout: FiniteDuration)(generateBatch: () => Fu[List[A]])(using
final class BatchProvider[A](name: String, timeout: FiniteDuration, monitor: AsyncActorBounded.Monitor)(
generateBatch: () => Fu[List[A]]
)(using
Executor,
Scheduler
):
@@ -11,7 +15,7 @@ final class BatchProvider[A](name: String, timeout: FiniteDuration)(generateBatc
maxSize = Max(4096),
timeout = timeout,
name = s"$name.batchProvider.workQueue",
lila.log.asyncActorMonitor.full
monitor = monitor
)
private var reserve = List.empty[A]
+6 -3
View File
@@ -51,7 +51,7 @@ object Form:
def stringIn[A](choices: Seq[A])(key: A => String): Mapping[A] =
stringIn(choices.map(key).toSet).transform[A](str => choices.find(c => str == key(c)).get, key)
def id[Id](size: Int, fixed: Option[Id])(exists: Id => Fu[Boolean])(using
def idWithSyncUniqueCheck[Id](size: Int, fixed: Option[Id])(exists: Id => Fu[Boolean])(using
sr: StringRuntime[Id],
rs: SameRuntime[String, Id]
): Mapping[Id] =
@@ -59,9 +59,12 @@ object Form:
.verifying("IDs must be made of ASCII letters and numbers", id => """(?i)^[a-z\d]+$""".r.matches(id))
.into[Id]
fixed match
case Some(fixedId) => field.verifying("The ID cannot be changed now", id => id == fixedId)
case Some(fixedId) => field.verifying("The ID cannot be changed now", _ == fixedId)
case None =>
field.verifying("This ID is already in use", id => !exists(id).await(1.second, "unique ID"))
field.verifying(
"This ID is already in use",
id => !scala.concurrent.Await.result(exists(id), 1.second)
)
def empty[T]: FieldMapping[Option[T]] =
given Formatter[Option[T]] = new:
+3 -4
View File
@@ -13,7 +13,6 @@ object Lilakka:
val msg = s"$phase $name"
cs.addTask(phase, name): () =>
shutdownLogger.info(msg)
Chronometer(f())
.log(shutdownLogger)(_ => msg)
.result
.inject(akka.Done)
f().dmap: _ =>
shutdownLogger.info(s"$msg done")
akka.Done
+8 -13
View File
@@ -104,19 +104,14 @@ final class MarkdownRender(
Markdown(RawHtml.atUsernameRegex.replaceAllIn(markdown.value, "[@$1](/@/$1)"))
def apply(key: MarkdownRender.Key)(text: Markdown): Html = Html:
Chronometer
.sync:
try
val saferText = MarkdownRender.preventStackOverflow(text)
val withMentions = if sourceMap then saferText else mentionsToLinks(saferText)
renderer.render(parser.parse(withMentions.value))
catch
case e: StackOverflowError =>
logger.branch(key).error("StackOverflowError", e)
text.value
.mon(_.markdown.time)
.logIfSlow(50, logger.branch(key))(_ => s"slow markdown size:${text.value.size}")
.result
try
val saferText = MarkdownRender.preventStackOverflow(text)
val withMentions = if sourceMap then saferText else mentionsToLinks(saferText)
renderer.render(parser.parse(withMentions.value))
catch
case e: StackOverflowError =>
logger.branch(key).error("StackOverflowError", e)
text.value
object MarkdownRender:
-19
View File
@@ -12,22 +12,3 @@ object log:
def http(status: Int, body: String) =
s"$status ${body.linesIterator.take(1).toList.headOption.getOrElse("-")}"
object asyncActorMonitor:
lazy val full = scalalib.actor.AsyncActorBounded.Monitor(
overflow = name =>
lila.mon.asyncActor.overflow(name).increment()
lila.log("asyncActor").warn(s"[$name] queue is full")
,
queueSize = (name, size) => lila.mon.asyncActor.queueSize(name).record(size),
unhandled = (name, msg) => lila.log("asyncActor").warn(s"[$name] unhandled msg: $msg")
)
lazy val highCardinality = full.copy(
queueSize = (_, _) => ()
)
lazy val unhandled = full.copy(
overflow = _ => (),
queueSize = (_, _) => ()
)
-759
View File
@@ -1,759 +0,0 @@
package lila
import com.github.benmanes.caffeine.cache.Cache as CaffeineCache
import kamon.metric.{ Counter, Timer }
import kamon.tag.TagSet
import chess.variant.Variant
import lila.core.id.*
import lila.core.net.*
import lila.core.userId.{ UserId, UserName }
import lila.core.perf.PerfKey
object mon:
import kamon.Kamon.{ timer, gauge, counter, histogram }
// https://github.com/kamon-io/Kamon/issues/752
extension (s: String)
def escape: String =
val builder = java.lang.StringBuilder(s.length)
for c <- s.toCharArray do
if c != '"' && c != '\n' && c != '\\'
then builder.append(c)
builder.toString
private def tags(elems: (String, Any)*): Map[String, Any] = Map.from(elems)
object http:
private val reqTime = timer("http.time")
private val reqCount = counter("http.count")
private val mobCount = counter("http.mobile.count")
def time(action: String) = reqTime.withTag("action", action)
def count(action: String, client: String, method: String, code: Int) =
reqCount.withTags:
tags("action" -> action, "client" -> client, "method" -> method, "code" -> code.toLong)
def errorCount(action: String, client: String, method: String, code: Int) =
counter("http.error").withTags:
tags("action" -> action, "client" -> client, "method" -> method, "code" -> code.toLong)
def mobileCount(action: String, version: String, auth: Boolean, os: String) =
mobCount.withTags:
tags(
"action" -> action,
"version" -> version,
"auth" -> (if auth then "auth" else "anon"),
"os" -> os
)
def path(p: String) = counter("http.path.count").withTag("path", p.escape)
val userGamesCost = counter("http.userGames.cost").withoutTags()
def csrfError(tpe: String, action: String, client: String) =
counter("http.csrf.error").withTags(tags("type" -> tpe, "action" -> action, "client" -> client))
val fingerPrint = timer("http.fingerPrint.time").withoutTags()
object syncache:
def miss(name: String) = counter("syncache.miss").withTag("name", name)
def timeout(name: String) = counter("syncache.timeout").withTag("name", name)
def compute(name: String) = timer("syncache.compute").withTag("name", name)
def wait(name: String) = timer("syncache.wait").withTag("name", name)
def caffeineStats(cache: CaffeineCache[?, ?], name: String): Unit =
val stats = cache.stats
gauge("caffeine.request").withTags(tags("name" -> name, "hit" -> true)).update(stats.hitCount.toDouble)
gauge("caffeine.request").withTags(tags("name" -> name, "hit" -> false)).update(stats.missCount.toDouble)
histogram("caffeine.hit.rate").withTag("name", name).record((stats.hitRate * 100000).toLong)
if stats.totalLoadTime > 0 then
gauge("caffeine.load.count")
.withTags(tags("name" -> name, "success" -> "success"))
.update(stats.loadSuccessCount.toDouble)
gauge("caffeine.load.count")
.withTags(tags("name" -> name, "success" -> "failure"))
.update(stats.loadFailureCount.toDouble)
gauge("caffeine.loadTime.cumulated")
.withTag("name", name)
.update(stats.totalLoadTime / 1000000d) // in millis; too much nanos for Kamon to handle)
timer("caffeine.loadTime.penalty").withTag("name", name).record(stats.averageLoadPenalty.toLong)
gauge("caffeine.eviction.count").withTag("name", name).update(stats.evictionCount.toDouble)
gauge("caffeine.entry.count").withTag("name", name).update(cache.estimatedSize.toDouble)
object mongoCache:
def request(name: String, hit: Boolean) =
counter("mongocache.request").withTags:
tags(
"name" -> name,
"hit" -> hit
)
def compute(name: String) = timer("mongocache.compute").withTag("name", name)
object evalCache:
private val r = counter("evalCache.request")
def request(ply: Int, isHit: Boolean) =
r.withTags(tags("ply" -> (if ply < 15 then ply.toString else "15+"), "hit" -> isHit))
object upgrade:
val count = counter("evalCache.upgrade.count").withoutTags()
val members = gauge("evalCache.upgrade.members").withoutTags()
val evals = gauge("evalCache.upgrade.evals").withoutTags()
val expirable = gauge("evalCache.upgrade.expirable").withoutTags()
object lobby:
object hook:
val create = counter("lobby.hook.create").withoutTags()
val join = counter("lobby.hook.join").withoutTags()
val size = histogram("lobby.hook.size").withoutTags()
def apiCreate(client: String) = counter("lobby.hook.apiCreate").withTag("client", client)
object seek:
val create = counter("lobby.seek.create").withoutTags()
val join = counter("lobby.seek.join").withoutTags()
object socket:
val getSris = timer("lobby.socket.getSris").withoutTags()
val member = gauge("lobby.socket.member").withoutTags()
val idle = gauge("lobby.socket.idle").withoutTags()
val hookSubscribers = gauge("lobby.socket.hookSubscribers").withoutTags()
object pool:
object wave:
def scheduled(id: String) = counter("lobby.pool.wave.scheduled").withTag("pool", id)
def full(id: String) = counter("lobby.pool.wave.full").withTag("pool", id)
def candidates(id: String) = histogram("lobby.pool.wave.candidates").withTag("pool", id)
def paired(id: String) = histogram("lobby.pool.wave.paired").withTag("pool", id)
def missed(id: String) = histogram("lobby.pool.wave.missed").withTag("pool", id)
def ratingDiff(id: String) = histogram("lobby.pool.wave.ratingDiff").withTag("pool", id)
def withRange(id: String) = histogram("lobby.pool.wave.withRange").withTag("pool", id)
object thieve:
def stolen(id: String) = histogram("lobby.pool.thieve.stolen").withTag("pool", id)
private val lobbySegment = timer("lobby.segment")
def segment(seg: String) = lobbySegment.withTag("segment", seg)
object rating:
def distribution(perfKey: PerfKey, rating: Int) =
gauge("rating.distribution").withTags(tags("perf" -> perfKey, "rating" -> rating.toLong))
object regulator:
def micropoints(perfKey: PerfKey) = histogram("rating.regulator").withTag("perf", perfKey.value)
object perfStat:
def indexTime = timer("perfStat.indexTime").withoutTags()
object round:
object api:
val player = timer("round.api").withTag("endpoint", "player")
val watcher = timer("round.api").withTag("endpoint", "watcher")
object forecast:
val create = counter("round.forecast.create").withoutTags()
object move:
object lag:
val compDeviation = histogram("round.move.lag.comp_deviation").withoutTags()
def uncomped(key: String) = histogram("round.move.lag.uncomped_ms").withTag("key", key)
def uncompStdDev(key: String) = histogram("round.move.lag.uncomp_stdev_ms").withTag("key", key)
val stdDev = histogram("round.move.lag.stddev_ms").withoutTags()
val mean = histogram("round.move.lag.mean_ms").withoutTags()
val coefVar = histogram("round.move.lag.coef_var_1000").withoutTags()
val compEstStdErr = histogram("round.move.lag.comp_est_stderr_1000").withoutTags()
val compEstOverErr = histogram("round.move.lag.avg_over_error_ms").withoutTags()
val moveComp = timer("round.move.lag.comped").withoutTags()
val time = timer("round.move.time").withoutTags()
object error:
val client = counter("round.error").withTag("from", "client")
val fishnet = counter("round.error").withTag("from", "fishnet")
val glicko = counter("round.error").withTag("from", "glicko")
val other = counter("round.error").withTag("from", "other")
object titivate:
val time = future("round.titivate.time")
val game = histogram("round.titivate.game").withoutTags() // how many games were processed
val total = histogram("round.titivate.total").withoutTags() // how many games should have been processed
val old = histogram("round.titivate.old").withoutTags() // how many old games remain
def broken(error: String) = counter("round.titivate.broken").withTag("error", error) // broken game
object alarm:
val time = timer("round.alarm.time").withoutTags()
object expiration:
val count = counter("round.expiration.count").withoutTags()
val asyncActorCount = gauge("round.asyncActor.count").withoutTags()
object correspondenceEmail:
val emails = histogram("round.correspondenceEmail.emails").withoutTags()
val time = future("round.correspondenceEmail.time")
object farming:
val bot = counter("round.farming.bot").withoutTags()
val provisional = counter("round.farming.provisional").withoutTags()
object playban:
def outcome(out: String) = counter("playban.outcome").withTag("outcome", out)
object ban:
val count = counter("playban.ban.count").withoutTags()
val mins = histogram("playban.ban.mins").withoutTags()
object explorer:
object index:
def count(success: Boolean) = counter("explorer.index.count").withTag("success", successTag(success))
val time = timer("explorer.index.time").withoutTags()
object timeline:
val notification = counter("timeline.notification").withoutTags()
object insight:
val user = future("insight.request.time", "user")
val peers = future("insight.request.time", "peer")
val index = future("insight.index.time")
object tutor:
def buildSegment(segment: String) = future("tutor.build.segment", segment)
val buildFull = future("tutor.build.full")
val askMine = askAs("mine")
val askPeer = askAs("peer")
val buildTimeout = counter("tutor.build.timeout").withoutTags()
def peerMatch(hit: Boolean, perf: PerfKey) = counter("tutor.peerMatch").withTags:
tags("hit" -> hitTag(hit), "perf" -> perf)
val parallelism = gauge("tutor.build.parallelism").withoutTags()
val fishnetMissing = histogram("tutor.fishnet.missing").withoutTags()
private def askAs(as: "mine" | "peer")(question: String, perf: PerfKey | "all") =
future("tutor.insight.ask", tags("question" -> question, "perf" -> perf, "as" -> as))
object search:
def time(op: "search" | "count", index: String, success: Boolean) =
timer("search.client.time").withTags:
tags(
"op" -> op,
"index" -> index,
"success" -> successTag(success)
)
object asyncActor:
def overflow(name: String) = counter("asyncActor.overflow").withTag("name", name)
def queueSize(name: String) = histogram("asyncActor.queueSize").withTag("name", name)
object irc:
object zulip:
def say(stream: String) = future("irc.zulip.say", tags("stream" -> stream.escape))
object user:
val online = gauge("user.online").withoutTags()
object register:
def count(
confirm: String,
ipSusp: Boolean,
fp: Boolean,
proxy: Option[String],
country: String,
client: String
) =
counter("user.register.count").withTags:
tags(
"confirm" -> confirm,
"ipSusp" -> ipSusp,
"fp" -> fp,
"proxy" -> proxy.getOrElse("no"),
"country" -> country.escape,
"client" -> client
)
def result(client: String, result: String) =
counter("user.register.result").withTags:
tags("client" -> client, "result" -> result)
def mustConfirmEmail(v: String) = counter("user.register.mustConfirmEmail").withTag("type", v)
def confirmEmailResult(success: Boolean) =
counter("user.register.confirmEmail").withTag("success", successTag(success))
def modConfirmEmail(by: "mod" | "worker", result: String) =
counter("user.register.modConfirmEmail").withTags:
tags("by" -> by, "result" -> result)
object auth:
val bcFullMigrate = counter("user.auth.bcFullMigrate").withoutTags()
val hashTime = timer("user.auth.hashTime").withoutTags()
def count(success: Boolean) = counter("user.auth.count").withTag("success", successTag(success))
def passwordResetRequest(s: String) = counter("user.auth.passwordResetRequest").withTag("type", s)
def passwordResetConfirm(s: String) = counter("user.auth.passwordResetConfirm").withTag("type", s)
def reopenRequest(s: String) = counter("user.auth.reopenRequest").withTag("type", s)
def reopenConfirm(s: String) = counter("user.auth.reopenConfirm").withTag("type", s)
object oauth:
def request(success: Boolean) = counter("user.oauth.request").withTags:
tags("success" -> successTag(success))
private val userSegment = timer("user.segment")
def segment(seg: String) = userSegment.withTag("segment", seg)
def leaderboardCompute = future("user.leaderboard.compute")
def weeklyStableRanking(perf: PerfKey) = future("user.weeklyStableRanking", perf.value)
object actor:
def queueSize(name: String) = gauge("trouper.queueSize").withTag("name", name)
object mod:
object report:
val highest = gauge("mod.report.highest").withoutTags()
val close = counter("mod.report.close").withoutTags()
def create(reason: String, score: Int) =
counter("mod.report.create").withTags:
tags("reason" -> reason, "score" -> score)
object automod:
val request = future("mod.report.automod.request")
def assessment(a: String) = counter("mod.report.automod.assessment").withTag("assessment", a)
val imageRequest = future("mod.report.automod.image.request")
def imageFlagged(v: Boolean) = counter("mod.report.automod.image.flagged").withTag("flagged", v)
object log:
val create = counter("mod.log.create").withoutTags()
object irwin:
val report = counter("mod.report.irwin.report").withoutTags()
val mark = counter("mod.report.irwin.mark").withoutTags()
def ownerReport(name: String) = counter("mod.irwin.ownerReport").withTag("name", name)
def streamEventType(name: String) = counter("mod.irwin.stream.eventType").withTag("name", name)
object kaladin:
def request(by: String) = counter("mod.kaladin.request").withTag("by", by)
def insufficientMoves(by: String) = counter("mod.kaladin.insufficientMoves").withTag("by", by)
def queue(priority: Int) = gauge("mod.kaladin.queue").withTag("priority", priority)
def error(errKind: String) = counter("mod.kaladin.error").withTag("error", errKind)
val activation = histogram("mod.report.kaladin.activation").withoutTags()
val report = counter("mod.report.kaladin.report").withoutTags()
val mark = counter("mod.report.kaladin.mark").withoutTags()
object comm:
def segment(seg: String) = timer("mod.comm.segmentLat").withTag("segment", seg)
def zoneSegment(name: String) = future("mod.zone.segment", name)
object relay:
private def by(official: Boolean) = if official then "official" else "user"
private def relay(official: Boolean, id: RelayTourId, slug: String) =
tags("by" -> by(official), "slug" -> s"$slug/$id")
def ongoing(official: Boolean) = gauge("relay.ongoing").withTag("by", by(official))
val crowdMonitor = gauge("relay.crowdMonitor").withoutTags()
def moves(official: Boolean, id: RelayTourId, slug: String) =
counter("relay.moves").withTags(relay(official, id, slug))
def fetchTime(official: Boolean, id: RelayTourId, slug: String) =
timer("relay.fetch.time").withTags(relay(official, id, slug))
def syncTime(official: Boolean, id: RelayTourId, slug: String) =
timer("relay.sync.time").withTags(relay(official, id, slug))
def httpGet(code: Int, host: String, etag: String, proxy: Option[String]) =
timer("relay.http.get").withTags:
tags(
"code" -> code.toLong,
"host" -> host.escape,
"etag" -> etag.escape,
"proxy" -> proxy.getOrElse("none")
)
val dedup = counter("relay.fetch.dedup").withoutTags()
def push(name: String, user: UserName, client: String)(games: Int, moves: Int, errors: Int) =
val histogramTags = tags("name" -> name.escape, "user" -> user, "client" -> client.escape)
val counterTags = tags("name" -> name.escape, "user" -> user)
histogram("relay.push.games").withTags(histogramTags).record(games)
histogram("relay.push.moves").withTags(histogramTags).record(moves)
histogram("relay.push.errors").withTags(histogramTags).record(errors)
counter("relay.push.games.nb").withTags(counterTags).increment(games)
counter("relay.push.moves.nb").withTags(counterTags).increment(moves)
object bot:
def moves(username: String) = counter("bot.moves").withTag("name", username)
def chats(username: String) = counter("bot.chats").withTag("name", username)
def gameStream(event: "start" | "stop") = counter("bot.gameStream").withTag("event", event)
object cheat:
def selfReport(wildName: String, auth: Boolean) =
val name = if wildName.startsWith("soc: ") then "soc" else wildName.takeWhile(' ' !=)
counter("cheat.selfReport").withTags(tags("name" -> name.escape, "auth" -> auth))
val holdAlert = counter("cheat.holdAlert").withoutTags()
def autoAnalysis(reason: String) = counter("cheat.autoAnalysis").withTag("reason", reason)
val autoMark = counter("cheat.autoMark.count").withoutTags()
val autoReport = counter("cheat.autoReport.count").withoutTags()
object email:
object send:
private val c = counter("email.send")
val resetPassword = c.withTag("type", "resetPassword")
val magicLink = c.withTag("type", "magicLink")
val reopen = c.withTag("type", "reopen")
val fix = c.withTag("type", "fix")
val change = c.withTag("type", "change")
val confirmation = c.withTag("type", "confirmation")
val welcome = c.withTag("type", "welcome")
def time(mailer: String) = future("email.send.time", tags("mailer" -> mailer))
val disposableDomain = gauge("email.disposableDomain").withoutTags()
object security:
val torNodes = gauge("security.tor.node").withoutTags()
object firewall:
val block = counter("security.firewall.block").withoutTags()
val ip = gauge("security.firewall.ip").withoutTags()
val prints = gauge("security.firewall.prints").withoutTags()
object proxy:
val request = future("security.proxy.time")
def result(r: Option[String]) = counter("security.proxy.result").withTag("result", r.getOrElse("none"))
def hit(prox: String, action: String) =
counter("security.proxy.hit").withTags(tags("proxy" -> prox, "action" -> action))
def rateLimit(key: String) = counter("security.rateLimit.count").withTag("key", key)
def concurrencyLimit(key: String) = counter("security.concurrencyLimit.count").withTag("key", key)
object dnsApi:
val mx = future("security.dnsApi.mx.time")
object verifyMailApi:
def fetch(success: Boolean, ok: Boolean) =
timer("verifyMail.fetch").withTags(tags("success" -> successTag(success), "ok" -> ok))
object mailcheckApi:
def fetch(success: Boolean, ok: Boolean) =
timer("mailcheck.fetch").withTags(tags("success" -> successTag(success), "ok" -> ok))
object turnstile:
def hit(client: String, action: String, result: String) =
counter("turnstile.hit").withTags(tags("client" -> client, "action" -> action, "result" -> result))
object pwned:
def get(res: Boolean) = timer("security.pwned.result").withTag("res", res)
object geoip:
val epoch = gauge("security.geoip.epoch").withoutTags()
val loadTime = gauge("security.geoip.loadTime").withoutTags()
object login:
def attempt(byEmail: Boolean, pwned: Boolean, result: Boolean) =
counter("security.login.attempt").withTags:
tags(
"by" -> (if byEmail then "email" else "name"),
"pwned" -> pwned,
"result" -> result
)
def proxy(tpe: String) = counter("security.login.proxy").withTag("proxy", tpe)
def secretScanning(tokenType: String, source: String, hit: Boolean) =
counter("security.githubSecretScanning.hit").withTags(
tags("type" -> tokenType, "source" -> source.escape, "hit" -> hit)
)
def userTrust(trust: Boolean, cause: String) =
counter("security.userTrust").withTags(tags("trust" -> trust, "cause" -> cause)).increment()
object shutup:
def analyzer = timer("shutup.analyzer.time").withoutTags()
object tv:
object selector:
def candidates(channel: String) = histogram("tv.selector.candidates").withTag("channel", channel)
def cheats(channel: String) = histogram("tv.selector.cheats").withTag("channel", channel)
def rating(channel: String) = histogram("tv.selector.rating").withTag("channel", channel)
object streamer:
def online = gauge("tv.streamer.count").withoutTags()
def present(n: String) = gauge("tv.streamer.present").withTag("name", n.escape)
object relation:
private val c = counter("relation.action")
val follow = c.withTag("type", "follow")
val unfollow = c.withTag("type", "unfollow")
val block = c.withTag("type", "block")
val unblock = c.withTag("type", "unblock")
object clas:
object student:
def create(teacher: UserId) = counter("clas.student.create").withTag("teacher", teacher)
def invite(teacher: UserId) = counter("clas.student.invite").withTag("teacher", teacher)
final class bloomFilter(name: String):
def count = gauge(s"clas.${name}.bloomFilter.count").withoutTags()
def fu = future(s"clas.${name}.bloomFilter.future")
object tournament:
object pairing:
val batchSize = histogram("tournament.pairing.batchSize").withoutTags()
val create = future("tournament.pairing.create")
val createRanking = timer("tournament.pairing.create.ranking").withoutTags()
val createPairings = timer("tournament.pairing.create.pairings").withoutTags()
val createPlayerMap = timer("tournament.pairing.create.playerMap").withoutTags()
val createInserts = timer("tournament.pairing.create.inserts").withoutTags()
val createFeature = timer("tournament.pairing.create.feature").withoutTags()
val createAutoPairing = timer("tournament.pairing.create.autoPairing").withoutTags()
val prep = future("tournament.pairing.prep")
val wmmatching = timer("tournament.pairing.wmmatching").withoutTags()
val created = gauge("tournament.count").withTag("type", "created")
val started = gauge("tournament.count").withTag("type", "started")
val waitingPlayers = histogram("tournament.waitingPlayers").withoutTags()
object startedOrganizer:
val tick = future("tournament.startedOrganizer.tick")
val waitingUsers = future("tournament.startedOrganizer.waitingUsers")
object createdOrganizer:
val tick = future("tournament.createdOrganizer.tick")
object lilaHttp:
val tick = future("tournament.lilaHttp.tick")
val fullSize = histogram("tournament.lilaHttp.fullSize").withoutTags()
val nbTours = gauge("tournament.lilaHttp.nbTours").withoutTags()
def apiShowPartial(partial: Boolean, client: String)(success: Boolean) =
timer("tournament.api.show").withTags:
tags(
"partial" -> partial,
"success" -> successTag(success),
"client" -> client
)
def withdrawableIds(reason: String) = future("tournament.withdrawableIds", reason)
def action(tourId: String, action: String) =
timer("tournament.api.action").withTags(tags("tourId" -> tourId, "action" -> action))
object notifier:
def tournaments = counter("tournament.notify.tournaments").withoutTags()
def players = counter("tournament.notify.players").withoutTags()
object featuring:
def forTeams(page: "index" | "homepage") = future("tournament.featuring.forTeams", page)
object swiss:
val tick = future("swiss.tick")
val bbpairing = timer("swiss.bbpairing").withoutTags()
val scoringGet = future("swiss.scoring.get")
val scoringRecompute = future("swiss.scoring.recompute")
val startRound = future("swiss.director.startRound")
def games(status: String) = histogram("swiss.ongoingGames").withTag("status", status)
val json = future("swiss.json")
object plan:
object paypalLegacy:
val amount = histogram("plan.amount").withTag("service", "paypal")
object paypalCheckout:
val amount = histogram("plan.amount").withTag("service", "paypalCheckout")
val fetchAccessToken = future("plan.paypal.accessToken")
val stripe = histogram("plan.amount").withTag("service", "stripe")
val goal = gauge("plan.goal").withoutTags()
val current = gauge("plan.current").withoutTags()
val percent = gauge("plan.percent").withoutTags()
def webhook(service: String, tpe: String) =
counter("plan.webhook").withTags(tags("service" -> service, "tpe" -> tpe))
def intent(service: String, currency: java.util.Currency, coverFees: Boolean) =
counter("plan.intent").withTags:
tags("service" -> service, "currency" -> currency.getCurrencyCode, "coverFees" -> coverFees)
object charge:
def first(service: String) = counter("plan.charge.first").withTag("service", service)
def countryCents(country: String, currency: java.util.Currency, service: String, gift: Boolean) =
histogram("plan.charge.country.cents").withTags:
tags(
"country" -> country.escape,
"currency" -> currency.getCurrencyCode,
"service" -> service,
"gift" -> gift
)
object forum:
object post:
val create = counter("forum.post.create").withoutTags()
object topic:
val view = counter("forum.topic.view").withoutTags()
def reaction(r: String) = counter("forum.reaction").withTag("reaction", r)
object msg:
def post(verdict: String, isNew: Boolean, multi: Boolean) = counter("msg.post").withTags(
tags("verdict" -> verdict, "isNew" -> isNew, "multi" -> multi)
)
val teamBulk = histogram("msg.bulk.team").withoutTags()
def clasBulk(clasId: ClasId) = histogram("msg.bulk.clas").withTag("id", clasId.value)
object puzzle:
object selector:
object user:
def time(categ: String) = timer("puzzle.selector.user.puzzle").withTag("categ", categ)
def retries(categ: String) = histogram("puzzle.selector.user.retries").withTag("categ", categ)
val vote = histogram("puzzle.selector.user.vote").withoutTags()
def tier(t: String, categ: String, difficulty: String) =
counter("puzzle.selector.user.tier").withTags:
tags("tier" -> t, "categ" -> categ, "difficulty" -> difficulty)
def batch(nb: Int) = timer("puzzle.selector.user.batch").withTag("nb", nb)
object anon:
val time = timer("puzzle.selector.anon.puzzle").withoutTags()
def batch(nb: Int) = timer("puzzle.selector.anon.batch").withTag("nb", nb)
val vote = histogram("puzzle.selector.anon.vote").withoutTags()
def nextPuzzleResult(result: String) =
timer("puzzle.selector.user.puzzleResult").withTag("result", result)
def nextPathFor(categ: String, requester: String) =
timer("puzzle.path.nextFor").withTags(tags("categ" -> categ, "requester" -> requester))
object batch:
object selector:
val count = counter("puzzle.batch.selector.count").withoutTags()
val time = timer("puzzle.batch.selector").withoutTags()
val solve = counter("puzzle.batch.solve").withoutTags()
object round:
def attempt(user: Boolean, theme: String, rated: Boolean) =
counter("puzzle.attempt.count").withTags(tags("user" -> user, "theme" -> theme, "rated" -> rated))
object vote:
def count(up: Boolean, win: Boolean) =
counter("puzzle.vote.count").withTags:
tags(
"up" -> up,
"win" -> win
)
def theme(key: String, up: Option[Boolean], win: Boolean) =
counter("puzzle.vote.theme").withTags:
tags(
"up" -> up.fold("cancel")(_.toString),
"theme" -> key,
"win" -> win
)
val future = mon.future("puzzle.vote.future")
val crazyGlicko = counter("puzzle.crazyGlicko").withoutTags()
object storm:
object selector:
val time = future("storm.selector.time")
val sets = histogram("storm.selector.sets").withoutTags()
val count = histogram("storm.selector.count").withoutTags()
val rating = histogram("storm.selector.rating").withoutTags()
def ratingSlice(index: Int) = histogram("storm.selector.ratingSlice").withTag("index", index)
object run:
def score(auth: Boolean) = histogram("storm.run.score").withTag("auth", auth)
def sign(cause: String) = counter("storm.run.sign").withTag("cause", cause)
object racer:
private def tpe(lobby: Boolean) = if lobby then "lobby" else "friend"
def race(lobby: Boolean) = counter("racer.lobby.race").withTag("tpe", tpe(lobby))
def players(lobby: Boolean) =
histogram("racer.lobby.players").withTag("tpe", tpe(lobby))
def score(lobby: Boolean, auth: Boolean) =
histogram("racer.player.score").withTags:
tags(
"tpe" -> tpe(lobby),
"auth" -> auth
)
object streak:
object selector:
val time = timer("streak.selector.time").withoutTags()
val count = histogram("streak.selector.count").withoutTags()
val rating = histogram("streak.selector.rating").withoutTags()
def ratingSlice(index: Int) = histogram("streak.selector.ratingSlice").withTag("index", index)
object run:
def score(auth: String) = histogram("streak.run.score").withTag("auth", auth)
object game:
import chess.{ Speed, Rated, Status }
import lila.core.game.Source
def finish(variant: Variant, speed: Speed, source: Option[Source], mode: Rated, status: Status) =
counter("game.finish").withTags:
tags(
"variant" -> variant.key,
"speed" -> speed.key,
"source" -> source.fold("unknown")(_.name),
"mode" -> mode.name,
"status" -> status.name
)
val fetch = counter("game.fetch.count").withoutTags()
val loadClockHistory = counter("game.loadClockHistory.count").withoutTags()
object pgn:
def encode(format: String) = timer("game.pgn.encode").withTag("format", format)
def decode(format: String) = timer("game.pgn.decode").withTag("format", format)
val idCollision = counter("game.idCollision").withoutTags()
def idGenerator(collisions: Int) = timer("game.idGenerator").withTags(tags("collisions" -> collisions))
object streamByOauthOrigin:
def event(tpe: String) = counter("game.streamByOauthOrigin.event").withTag("type", tpe)
def users(sel: String) = gauge("game.streamByOauthOrigin.users").withTag("selector", sel)
def streams(ua: UserAgent) = gauge("game.streamByOauthOrigin.streams").withTag("ua", ua.value)
val bloomFP = counter("game.streamByOauthOrigin.bloomFP").withoutTags()
object chat:
private val msgCounter = counter("chat.message")
def message(parent: String, troll: Boolean) =
msgCounter.withTags(tags("parent" -> parent, "troll" -> troll))
def fetch(parent: String) = timer("chat.fetch").withTag("parent", parent)
object push:
object register:
def in(platform: String) = counter("push.register").withTag("platform", platform)
val out = counter("push.register.out").withoutTags()
object web:
def post = future("push.web.post")
object send:
private def send(tpe: String)(platform: String, success: Boolean, count: Int): Unit =
counter("push.send")
.withTags:
tags(
"type" -> tpe,
"platform" -> platform,
"success" -> successTag(success)
)
.increment(count)
()
val move = send("move")
val takeback = send("takeback")
val draw = send("draw")
val corresAlarm = send("corresAlarm")
val finish = send("finish")
val message = send("message")
val tourSoon = send("tourSoon")
val forumMention = send("forumMention")
val invitedStudy = send("invitedStudy")
val streamStart = send("streamStart")
val broadcastRound = send("broadcastRound")
object challenge:
val create = send("challengeCreate")
val accept = send("challengeAccept")
val googleTokenTime = timer("push.send.googleToken").withoutTags()
def firebaseStatus(project: String, typ: String, status: Int) =
counter("push.firebase.status").withTags(tags("status" -> status, "project" -> project, "type" -> typ))
object fishnet:
object client:
object result:
private val c = counter("fishnet.client.result")
private def apply(r: String)(client: UserId) =
c.withTags(tags("client" -> client, "result" -> r))
val success = apply("success")
val failure = apply("failure")
val timeout = apply("timeout")
val notFound = apply("notFound")
val notAcquired = apply("notAcquired")
val abort = apply("abort")
def status(enabled: Boolean) = gauge("fishnet.client.status").withTag("enabled", enabled)
def version(v: String) = gauge("fishnet.client.version").withTag("version", v.escape)
def queueTime(sender: "system" | "user") = timer("fishnet.queue.db").withTag("sender", sender)
val acquire = future("fishnet.acquire")
def work(typ: String, as: "system" | "user") =
gauge("fishnet.work").withTags(tags("type" -> typ, "for" -> as))
def oldest(as: "system" | "user") = gauge("fishnet.oldest").withTag("for", as)
object analysis:
object by:
def movetime(client: UserId) = histogram("fishnet.analysis.movetime").withTag("client", client)
def node(client: UserId) = histogram("fishnet.analysis.node").withTag("client", client)
def nps(client: UserId) = histogram("fishnet.analysis.nps").withTag("client", client)
def depth(client: UserId) = histogram("fishnet.analysis.depth").withTag("client", client)
def pvSize(client: UserId) = histogram("fishnet.analysis.pvSize").withTag("client", client)
def pv(client: UserId, isLong: Boolean) =
counter("fishnet.analysis.pvs").withTags(tags("client" -> client, "long" -> isLong))
def totalMeganode(client: UserId) =
counter("fishnet.analysis.total.meganode").withTag("client", client)
def totalSecond(client: UserId) =
counter("fishnet.analysis.total.second").withTag("client", client)
def requestCount(tpe: "game" | "study") = counter("fishnet.analysis.request").withTag("type", tpe)
val evalCacheHits = histogram("fishnet.analysis.evalCacheHits").withoutTags()
val skipPositionsGame = future("fishnet.analysis.skipPositions.game")
val skipPositionsStudy = future("fishnet.analysis.skipPositions.study")
object http:
def request(hit: Boolean) = counter("fishnet.http.acquire").withTag("hit", hit)
def move(level: Int) = counter("fishnet.move.time").withTag("level", level)
def openingBook(variant: Variant, hit: Boolean) =
timer("fishnet.opening.hit").withTags:
tags("variant" -> variant.key, "hit" -> hitTag(hit))
object opening:
def searchTime = timer("opening.search.time").withoutTags()
object explorer:
def stats = future("opening.explorer.stats")
object study:
object tree:
val read = timer("study.tree.read").withoutTags()
val write = timer("study.tree.write").withoutTags()
object sequencer:
val chapterTime = timer("study.sequencer.chapter.time").withoutTags()
object api:
val users = counter("api.cost").withTag("endpoint", "users")
val activity = counter("api.cost").withTag("endpoint", "activity")
object challenge:
object bulk:
def scheduleNb(by: UserId) = counter("api.challenge.bulk.schedule.nb").withTag("by", by)
def createNb(by: UserId) = counter("api.challenge.bulk.create.nb").withTag("by", by)
object `export`:
object png:
val game = counter("export.png").withTag("type", "game")
val puzzle = counter("export.png").withTag("type", "puzzle")
object bus:
val classifiers = gauge("bus.classifiers").withoutTags()
object blocking:
def time(name: String) = timer("blocking.time").withTag("name", name)
def timeout(name: String) = counter("blocking.timeout").withTag("name", name)
object workQueue:
def offerFail(name: String, result: String) =
counter("workQueue.offerFail").withTags:
tags("name" -> name, "result" -> result)
def timeout(name: String) = counter("workQueue.timeout").withTag("name", name)
class parallelQueue(name: String):
val parallelism = gauge("parallelQueue.parallelism").withTag("name", name)
val computeTimeout = counter("parallelQueue.buildTimeout").withTag("name", name)
object markdown:
val time = timer("markdown.time").withoutTags()
def pgnsFromText = future("markdown.pgnsFromText")
object ublog:
def create(user: UserId) = counter("ublog.create").withTag("user", user)
def view = counter("ublog.view").withoutTags()
object automod:
val request = future("ublog.automod.request")
def quality(q: String) = counter("ublog.automod.quality").withTag("quality", q)
def flagged(f: Boolean) = counter("ublog.automod.flagged").withTag("flagged", f)
object picfit:
def uploadTime(user: UserId) = future("picfit.upload.time", tags("user" -> user))
def uploadSize(user: UserId) = histogram("picfit.upload.size").withTag("user", user)
object fideSync:
val time = future("fide.sync.time")
val players = gauge("fide.sync.players").withoutTags()
val updated = gauge("fide.sync.updated").withoutTags()
object recap:
val games = future("recap.build.games.time")
val puzzles = future("recap.build.puzzles.time")
object jvm:
def threads() =
val perState = gauge("jvm.threads.group")
val total = gauge("jvm.threads.group.total")
for
group <- scalalib.Jvm.threadGroups()
_ = total.withTags(tags("name" -> group.name)).update(group.total)
(state, count) <- group.states
yield perState.withTags(tags("name" -> group.name, "state" -> state.toString)).update(count)
object prometheus:
val lines = gauge("prometheus.lines").withoutTags()
def chronoSync[A] = lila.common.Chronometer.syncMon[A]
type TimerPath = lila.mon.type => Timer
type CounterPath = lila.mon.type => Counter
private def future(name: String) = (success: Boolean) => timer(name).withTag("success", successTag(success))
private def future(name: String, tags: Map[String, Any]) = (success: Boolean) =>
timer(name).withTags(tags + ("success" -> successTag(success)))
private def future(name: String, segment: String)(success: Boolean) =
timer(name).withTags:
tags("success" -> successTag(success), "segment" -> segment)
private def successTag(success: Boolean) = if success then "success" else "failure"
private def hitTag(hit: Boolean) = if hit then "hit" else "miss"
import scala.language.implicitConversions
private given Conversion[UserId, String] = _.value
private given Conversion[Map[String, Any], TagSet] = TagSet.from
-1
View File
@@ -5,7 +5,6 @@ import scalalib.data.LazyFu
export lila.core.lilaism.Lilaism.{ *, given }
object extensions:
export Chronometer.futureExtension.*
// replaces Product.unapply in play forms
def unapply[P <: Product](p: P)(using m: scala.deriving.Mirror.ProductOf[P]): Option[m.MirroredElemTypes] =
Some(Tuple.fromProductTyped(p))
+13 -12
View File
@@ -2,7 +2,6 @@ package lila.db
import reactivemongo.api.*
import lila.common.Chronometer
import lila.core.config.CollName
import lila.db.dsl.Coll
@@ -37,16 +36,18 @@ final class Db(
private val logger = lila.db.logger.branch(name)
private lazy val db: DB = Chronometer.syncEffect(
MongoConnection
.fromString(uri)
.flatMap: parsedUri =>
driver
.connect(parsedUri, name.some)
.flatMap(_.database(parsedUri.db.getOrElse("lichess")))
.await(5.seconds, s"db:$name")
) { lap =>
logger.info(s"MongoDB connected to $uri in ${lap.showDuration}")
}
private lazy val db: DB =
logger.info(s"MongoDB connecting to $uri")
val connected = scala.concurrent.Await.result(
MongoConnection
.fromString(uri)
.flatMap: parsedUri =>
driver
.connect(parsedUri, name.some)
.flatMap(_.database(parsedUri.db.getOrElse("lichess"))),
5.seconds
)
logger.info(s"MongoDB connected to $uri")
connected
def apply(name: CollName): Coll = db.collection(name.value)
+2 -1
View File
@@ -9,6 +9,7 @@ import reactivemongo.api.bson.*
import java.util.zip.ZipInputStream
import java.time.YearMonth
import lila.mon.extensions.*
import lila.core.fide.Federation
import lila.db.dsl.{ *, given }
@@ -121,7 +122,7 @@ final private class FidePlayerSync(repo: FideRepo, ws: StandaloneWSClient)(using
.map(_.toList)
.mapAsync(1)(saveIfChanged)
.runWith(lila.common.LilaStream.sinkSum)
.monSuccess(_.fideSync.time)
.monSuccess(lila.mon.fideSync.time)
nbAll <- repo.player.countAll
yield
lila.mon.fideSync.updated.update(nbUpdated)
+3 -2
View File
@@ -3,6 +3,7 @@ package lila.fishnet
import chess.Ply
import scalalib.cache.OnceEvery
import lila.mon.extensions.*
import lila.analyse.AnalysisRepo
import lila.core.id
import lila.fishnet.Work.{ Origin, Sender }
@@ -68,7 +69,7 @@ final class Analyser(
lila.mon.fishnet.analysis.requestCount("game").increment()
evalCache
.skipPositions(work.game)
.monSuccess(_.fishnet.analysis.skipPositionsGame)
.monSuccess(lila.mon.fishnet.analysis.skipPositionsGame)
.flatMap: skipPositions =>
lila.mon.fishnet.analysis.evalCacheHits.record(skipPositions.size)
repo.addAnalysis(work.copy(skipPositions = skipPositions))
@@ -119,7 +120,7 @@ final class Analyser(
lila.mon.fishnet.analysis.requestCount("study").increment()
evalCache
.skipPositions(work.game)
.monSuccess(_.fishnet.analysis.skipPositionsStudy)
.monSuccess(lila.mon.fishnet.analysis.skipPositionsStudy)
.withTimeout(2.seconds, s"study analysis skipPositions $work")
.recoverDefault
.flatMap: skipPositions =>
+3 -2
View File
@@ -7,6 +7,7 @@ import scala.util.{ Failure, Success, Try }
import lila.core.lilaism.LilaNoStackTrace
import lila.core.net.IpAddress
import lila.mon.extensions.*
import lila.db.dsl.{ *, given }
import Client.Skill
@@ -30,7 +31,7 @@ final class FishnetApi(
maxSize = Max(256),
timeout = 5.seconds,
name = "fishnetApi",
lila.log.asyncActorMonitor.full
lila.mon.asyncActorMonitor.full
)
def keyExists(key: Client.Key) = repo.getEnabledClient(key).map(_.isDefined)
@@ -50,7 +51,7 @@ final class FishnetApi(
.match
case Skill.Move => fufail(s"Can't acquire a move directly on lichess! $client")
case Skill.Analysis | Skill.All => acquireAnalysis(client, slow)
.monSuccess(_.fishnet.acquire)
.monSuccess(lila.mon.fishnet.acquire)
.recover { case e: Exception =>
logger.error("Fishnet.acquire", e)
none
@@ -9,6 +9,7 @@ import play.api.libs.ws.StandaloneWSClient
import scalalib.ThreadLocalRandom
import lila.common.Json.given
import lila.mon.extensions.*
import lila.memo.SettingStore
final private class FishnetOpeningBook(
@@ -51,7 +52,7 @@ final private class FishnetOpeningBook(
none
}
.monTry: res =>
_.fishnet.openingBook(
lila.mon.fishnet.openingBook(
variant = game.variant,
hit = res.toOption.exists(_.isDefined)
)
@@ -11,6 +11,7 @@ import lila.analyse.Annotator
import lila.core.config.NetDomain
import lila.core.game.Player
import lila.core.id.GamePlayerId
import lila.mon.extensions.*
import JsonApi.*
import readers.given
+1
View File
@@ -8,6 +8,7 @@ import scala.util.Success
import lila.core.captcha.{ Captcha, CaptchaApi as ICaptchaApi, Solutions, WithCaptcha }
import lila.core.game.Game
import lila.mon.extensions.*
// only works with standard chess (not chess960)
final private class CaptchaApi(gameRepo: GameRepo)(using Executor) extends ICaptchaApi:
+14 -12
View File
@@ -5,23 +5,25 @@ import scalalib.SecureRandom
import lila.core.game.{ Game, NewGame }
import lila.core.id.GameId
import lila.common.BatchProvider
import lila.mon.extensions.*
import lila.db.dsl.{ *, given }
final class IdGenerator(gameRepo: GameRepo)(using Executor, Scheduler) extends lila.core.game.IdGenerator:
import lila.core.game.IdGenerator.*
private val batchProvider = BatchProvider[GameId]("idGenerator", timeout = 3.seconds): () =>
// must NOT use `games(nb)` for it would cause a deadlock
// due to `games` calling `game` which calls `batchProvider.one`
val ids = List.fill(256)(uncheckedGame).distinct
gameRepo.coll
.distinctEasy[GameId, List]("_id", $inIds(ids))
.monValue: collisions =>
_.game.idGenerator(collisions.size)
.map:
case Nil => ids
case collisions => ids.filterNot(collisions.contains)
private val batchProvider =
BatchProvider[GameId]("idGenerator", timeout = 3.seconds, lila.mon.asyncActorMonitor.full): () =>
// must NOT use `games(nb)` for it would cause a deadlock
// due to `games` calling `game` which calls `batchProvider.one`
val ids = List.fill(256)(uncheckedGame).distinct
gameRepo.coll
.distinctEasy[GameId, List]("_id", $inIds(ids))
.monValue: collisions =>
lila.mon.game.idGenerator(collisions.size)
.map:
case Nil => ids
case collisions => ids.filterNot(collisions.contains)
def game: Fu[GameId] = batchProvider.one
@@ -34,7 +36,7 @@ final class IdGenerator(gameRepo: GameRepo)(using Executor, Scheduler) extends l
gameRepo.coll
.distinctEasy[GameId, Set]("_id", $inIds(ids))
.monValue: collisions =>
_.game.idGenerator(collisions.size)
lila.mon.game.idGenerator(collisions.size)
.flatMap: collisions =>
games(collisions.size).dmap { _ ++ (ids.diff(collisions)) }
.map(_.toList)
+6 -7
View File
@@ -8,15 +8,17 @@ import lila.db.ByteArray
private object PgnStorage:
import lila.mon.Chronometer.syncMon as monitor
object OldBin:
def encode(sans: Vector[SanStr]) =
ByteArray:
monitor(_.game.pgn.encode("old")):
monitor(lila.mon.game.pgn.encode("old")):
format.pgn.Binary.writeMoves(sans).get
def decode(bytes: ByteArray, plies: Ply): Vector[SanStr] =
monitor(_.game.pgn.decode("old")):
monitor(lila.mon.game.pgn.decode("old")):
format.pgn.Binary.readMoves(bytes.value.toList, plies.value).get.toVector
object Huffman:
@@ -25,11 +27,11 @@ private object PgnStorage:
def encode(sans: Vector[SanStr]) =
ByteArray:
monitor(_.game.pgn.encode("huffman")):
monitor(lila.mon.game.pgn.encode("huffman")):
Encoder.encode(SanStr.raw(sans.toArray))
def decode(bytes: ByteArray, plies: Ply, id: GameId): Decoded =
monitor(_.game.pgn.decode("huffman")):
monitor(lila.mon.game.pgn.decode("huffman")):
val decoded =
try Encoder.decode(bytes.value, plies.value)
catch
@@ -72,6 +74,3 @@ private object PgnStorage:
castles: Castles, // irrelevant after game ends
halfMoveClock: HalfMoveClock // irrelevant after game ends
)
private def monitor[A](mon: lila.mon.TimerPath)(f: => A): A =
lila.common.Chronometer.syncMon(mon)(f)
+2 -6
View File
@@ -7,7 +7,6 @@ import java.io.ObjectInputStream
import java.util.Map as JMap
import scala.jdk.CollectionConverters.*
import lila.common.Chronometer
import lila.core.i18n.{ I18nKey, defaultLang }
object Registry:
@@ -26,11 +25,8 @@ object Registry:
.zipWithIndex
.foreach: (langs, i) =>
scheduler.scheduleOnce(i.seconds):
val lap = Chronometer.sync:
langs.foreach: lang =>
register(lang, loadSerialized(lang))
if i < 1 || mode.isProd
then logger.info(s"Loaded ${langs.size} languages in ${lap.showDuration}")
langs.foreach: lang =>
register(lang, loadSerialized(lang))
// for tests
private[i18n] def getAll(lang: Lang): Option[MessageMap] = all.get(lang)
+4 -3
View File
@@ -3,6 +3,7 @@ package lila.insight
import scalalib.HeapSort.botN
import lila.game.GameRepo
import lila.mon.extensions.*
final class InsightApi(
storage: InsightStorage,
@@ -41,14 +42,14 @@ final class InsightApi(
gameRepo.userPovsByGameIds(clusters.flatMap(_.gameIds).botN(4), user)
.map { Answer(question, clusters, _) }
}
.monSuccess(_.insight.user)
.monSuccess(lila.mon.insight.user)
def askPeers[X](question: Question[X], rating: MeanRating, nbGames: Max): Fu[Answer[X]] =
pipeline
.aggregate(question, Right(PeersRatingRange.of(rating)), withPovs = false, nbGames = nbGames)
.map: aggDocs =>
Answer(question, AggregationClusters(question, aggDocs), Nil)
.monSuccess(_.insight.peers)
.monSuccess(lila.mon.insight.peers)
def userStatus(user: User): Fu[UserStatus] =
indexer
@@ -65,7 +66,7 @@ final class InsightApi(
case _ => UserStatus.Fresh
def indexAll(user: User, force: Boolean): Funit =
for _ <- indexer.all(user, force).monSuccess(_.insight.index)
for _ <- indexer.all(user, force).monSuccess(lila.mon.insight.index)
yield userCache.put(user.id, computeUser(user.id))
def updateGame(g: Game) =
@@ -19,7 +19,7 @@ final private class InsightIndexer(
maxSize = Max(256),
timeout = 1.minute,
name = "insightIndexer",
lila.log.asyncActorMonitor.full
lila.mon.asyncActorMonitor.full
)
def all(user: User, force: Boolean): Funit =
+2 -1
View File
@@ -7,6 +7,7 @@ import play.api.libs.ws.JsonBodyReadables.*
import play.api.libs.ws.{ StandaloneWSClient, WSAuthScheme }
import lila.common.String.urlencode
import lila.mon.extensions.*
import lila.core.config.Secret
final private class ZulipClient(ws: StandaloneWSClient, config: ZulipClient.Config)(using
@@ -54,7 +55,7 @@ final private class ZulipClient(ws: StandaloneWSClient, config: ZulipClient.Conf
case JsSuccess(result, _) => fuccess(result.some)
case JsError(err) => fufail(s"[zulip]: $err, $msg ${res.status} ${res.body}")
case res => fufail(s"[zulip] $msg ${res.status} ${res.body}")
.monSuccess(_.irc.zulip.say(msg.stream))
.monSuccess(lila.mon.irc.zulip.say(msg.stream))
.logFailure(lila.log("zulip"))
.recoverDefault
+1 -1
View File
@@ -33,7 +33,7 @@ final class KaladinApi(
maxSize = Max(512),
timeout = 2.minutes,
name = "kaladinApi",
lila.log.asyncActorMonitor.full
lila.mon.asyncActorMonitor.full
)
private def sequence[A <: Matchable](user: Suspect)(f: Option[KaladinUser] => Fu[A]): Fu[A] =
+2 -1
View File
@@ -5,6 +5,7 @@ import scalalib.actor.SyncActor
import lila.common.Bus
import lila.core.pool.{ HookThieve, IsClockCompatible }
import lila.core.socket.{ Sri, Sris }
import lila.mon.extensions.*
final private class LobbySyncActor(
seekApi: SeekApi,
@@ -98,7 +99,7 @@ final private class LobbySyncActor(
.chronometer
.logIfSlow(100, logger): r =>
s"GetSris size=${r.sris.size}"
.mon(_.lobby.socket.getSris)
.mon(lila.mon.lobby.socket.getSris)
.result
.logFailure(logger, err => s"broom cannot get sris from socket: $err")
.foreach { this ! WithPromise(_, promise) }
+4 -3
View File
@@ -1,5 +1,7 @@
package lila.mailer
import scala.concurrent.blocking
import akka.actor.ActorSystem
import play.api.ConfigLoader
import play.api.libs.mailer.{ Email, SMTPConfiguration, SMTPMailer }
@@ -7,8 +9,7 @@ import scalatags.Text.all.{ html as htmlTag, * }
import scalatags.Text.tags2.title as titleTag
import org.apache.commons.mail.EmailException
import scala.concurrent.blocking
import lila.mon.extensions.*
import lila.common.String.html.nl2br
import lila.common.autoconfig.*
import lila.core.i18n.I18nKey.emails as trans
@@ -71,7 +72,7 @@ final class Mailer(
)
blocking:
client.mailer.send(email)
.monSuccess(_.email.send.time(client.toString))
.monSuccess(lila.mon.email.send.time(client.toString))
.recoverWith:
case _: EmailException if msg.to.normalize.value != msg.to.value =>
logger.warn(s"Email ${msg.to} is invalid, trying ${msg.to.normalize}")
+10 -4
View File
@@ -3,6 +3,7 @@ package lila.memo
import scalalib.future.TimeoutException
import lila.common.{ Bus, Markdown, MarkdownRender, MarkdownToastUi }
import lila.mon.extensions.*
import lila.core.config
import lila.core.misc.lpv.{ LpvEmbed, Lpv as LpvBus }
@@ -66,7 +67,7 @@ final class MarkdownCache(
.logIfSlow(300, logger): result =>
s"AllPgnsFromText for markdown $key - found ${result.size} embeds"
.result
.monSuccess(_.markdown.pgnsFromText)
.monSuccess(lila.mon.markdown.pgnsFromText)
.andThen:
case scala.util.Success(pgns) => cache.putAll(pgns)
.recoverWith:
@@ -93,9 +94,14 @@ final class MarkdownCache(
)
)
private def bodyProcessor(key: RenderKey, opts: MarkdownOptions): Markdown => Html =
if opts.toastUi then toastUiProcessor(key, opts)
else getRenderer(opts)(key)
private def bodyProcessor(key: RenderKey, opts: MarkdownOptions)(text: Markdown): Html =
lila.mon.Chronometer
.sync:
if opts.toastUi then toastUiProcessor(key, opts)(text)
else getRenderer(opts)(key)(text)
.mon(lila.mon.markdown.time)
.logIfSlow(50, logger.branch(key))(_ => s"slow markdown size:${text.value.size}")
.result
private def toastUiProcessor(key: RenderKey, opts: MarkdownOptions): Markdown => Html =
MarkdownToastUi.unescapeAtUsername.apply
+2 -1
View File
@@ -4,6 +4,7 @@ import com.github.blemale.scaffeine.AsyncLoadingCache
import reactivemongo.api.bson.*
import lila.db.dsl.{ *, given }
import lila.mon.extensions.*
import CacheApi.*
@@ -36,7 +37,7 @@ final class MongoCache[K, V: BSONHandler] private (
)
.inject(v)
}
.mon(_.mongoCache.compute(name))
.mon(lila.mon.mongoCache.compute(name))
case Some(entry) =>
lila.mon.mongoCache.request(name, hit = true).increment()
fuccess(entry.v)
+1 -1
View File
@@ -29,7 +29,7 @@ final class MongoRateLimit[K](
expiration = 1.minute,
timeout = 10.seconds,
name = s"$name.sequencer",
lila.log.asyncActorMonitor.highCardinality
lila.mon.asyncActorMonitor.highCardinality
)
private def makeDbKey(k: K) = s"ratelimit:$name:${keyToString(k)}"
@@ -50,7 +50,7 @@ final class ParallelMongoQueue[A: BSONHandler](
maxSize = Max(256),
timeout = 5.seconds,
s"$name.workQueue",
lila.log.asyncActorMonitor.full
lila.mon.asyncActorMonitor.full
)
/* Read the oldest <parallelism()> entries from the queue
+5 -4
View File
@@ -6,6 +6,7 @@ import java.util.concurrent.TimeUnit
import scala.util.Success
import lila.common.Uptime
import lila.mon.extensions.*
/** A synchronous cache from asynchronous computations. It will attempt to serve cached responses
* synchronously. If none is available, it starts an async computation, and either waits for the result or
@@ -37,7 +38,7 @@ final class Syncache[K, V](
new CacheLoader[K, Fu[V]]:
def load(k: K) =
compute(k)
.mon(_ => recCompute) // monitoring: record async time
.mon(recCompute) // monitoring: record async time
.recover { case e: Exception =>
logger.branch(s"syncache $name").warn(s"key=$k", e)
cache.invalidate(k)
@@ -79,15 +80,15 @@ final class Syncache[K, V](
private def waitForResult(k: K, fu: Fu[V], duration: FiniteDuration): V =
try
lila.common.Chronometer.syncMon(_ => recWait):
lila.mon.Chronometer.syncMon(recWait):
fu.await(duration, s"syncache:$name")
catch
case _: java.util.concurrent.TimeoutException =>
incTimeout()
default(k)
private val incMiss = (() => lila.mon.syncache.miss(name).increment())
private val incTimeout = (() => lila.mon.syncache.timeout(name).increment())
private val incMiss = () => lila.mon.syncache.miss(name).increment()
private val incTimeout = () => lila.mon.syncache.timeout(name).increment()
private val recWait = lila.mon.syncache.wait(name)
private val recCompute = lila.mon.syncache.compute(name)
+2 -1
View File
@@ -11,6 +11,7 @@ import scala.util.matching.Regex.quote
import scalalib.paginator.AdapterLike
import lila.common.Bus
import lila.mon.extensions.*
import lila.core.id.ImageId
import lila.db.dsl.{ *, given }
@@ -182,7 +183,7 @@ final class PicfitApi(
if image.size > 0 then lila.mon.picfit.uploadSize(image.user).record(image.size)
funit
}
.monSuccess(_.picfit.uploadTime(image.user))
.monSuccess(lila.mon.picfit.uploadTime(image.user))
def delete(image: PicfitImage): Funit =
ws.url(s"${config.endpointPost}/${image.id}")
@@ -0,0 +1,25 @@
package lila.mon
import play.api.Logger
object asyncActorMonitor:
private lazy val logger = Logger("asyncActor")
lazy val full = scalalib.actor.AsyncActorBounded.Monitor(
overflow = name =>
lila.mon.asyncActor.overflow(name).increment()
logger.warn(s"[$name] queue is full")
,
queueSize = (name, size) => lila.mon.asyncActor.queueSize(name).record(size),
unhandled = (name, msg) => logger.warn(s"[$name] unhandled msg: $msg")
)
lazy val highCardinality = full.copy(
queueSize = (_, _) => ()
)
lazy val unhandled = full.copy(
overflow = _ => (),
queueSize = (_, _) => ()
)
@@ -1,4 +1,11 @@
package lila.common
package lila.mon
import scala.concurrent.Future
import scala.concurrent.duration.FiniteDuration
import kamon.metric.Timer
import play.api.LoggerLike
import lila.core.lilaism.Lilaism.*
object Chronometer:
@@ -8,7 +15,7 @@ object Chronometer:
extension [A](fua: Future[A])
def await(duration: FiniteDuration, name: String): A =
Chronometer.syncMon(_.blocking.time(name)):
Chronometer.syncMon(lila.mon.blocking.time(name)):
try Await.result(fua, duration)
catch
case e: Exception =>
@@ -21,15 +28,15 @@ object Chronometer:
def chronometer = Chronometer(fua)
def chronometerTry = Chronometer.lapTry(fua)
def mon(path: lila.mon.TimerPath): Fu[A] = chronometer.mon(path).result
def monTry(path: scala.util.Try[A] => lila.mon.TimerPath): Fu[A] =
chronometerTry.mon(r => path(r)(lila.mon)).result
def monSuccess(path: lila.mon.type => Boolean => kamon.metric.Timer): Fu[A] =
def mon(path: Timer): Fu[A] = chronometer.mon(path).result
def monTry(path: scala.util.Try[A] => Timer): Fu[A] =
chronometerTry.mon(r => path(r)).result
def monSuccess(path: Boolean => kamon.metric.Timer): Fu[A] =
chronometerTry
.mon: r =>
path(lila.mon)(r.isSuccess)
path(r.isSuccess)
.result
def monValue(path: A => lila.mon.TimerPath): Fu[A] = chronometer.monValue(path).result
def monValue(path: A => Timer): Fu[A] = chronometer.monValue(path).result
def logTime(name: String): Fu[A] = chronometer.pp(name)
def logTimeIfGt(name: String, duration: FiniteDuration): Fu[A] = chronometer.ppIfGt(name, duration)
@@ -41,19 +48,19 @@ object Chronometer:
def micros = (nanos / 1000).toInt
def seconds = (millis / 1000).toInt
def logIfSlow(threshold: Int, logger: lila.log.Logger)(msg: A => String) =
def logIfSlow(threshold: Int, logger: LoggerLike)(msg: A => String) =
if millis >= threshold then log(logger)(msg)
else this
def log(logger: lila.log.Logger)(msg: A => String) =
def log(logger: LoggerLike)(msg: A => String) =
logger.info(s"<${millis}ms> ${msg(result)}")
this
def mon(path: lila.mon.TimerPath) =
path(lila.mon).record(nanos)
def mon(path: Timer) =
path.record(nanos)
this
def monValue(path: A => lila.mon.TimerPath) =
path(result)(lila.mon).record(nanos)
def monValue(path: A => Timer) =
path(result).record(nanos)
this
def pp: A =
@@ -74,19 +81,19 @@ object Chronometer:
case class FuLap[A](lap: Fu[Lap[A]]) extends AnyVal:
def logIfSlow(threshold: Int, logger: lila.log.Logger)(msg: A => String) =
def logIfSlow(threshold: Int, logger: LoggerLike)(msg: A => String) =
lap.dforeach(_.logIfSlow(threshold, logger)(msg))
this
def mon(path: lila.mon.TimerPath) =
def mon(path: Timer) =
lap.dforeach(_.mon(path))
this
def monValue(path: A => lila.mon.TimerPath) =
def monValue(path: A => Timer) =
lap.dforeach(_.monValue(path))
this
def log(logger: lila.log.Logger)(msg: A => String) =
def log(logger: LoggerLike)(msg: A => String) =
lap.dforeach(_.log(logger)(msg))
this
@@ -133,8 +140,8 @@ object Chronometer:
effect(lap)
lap.result
def syncMon[A](path: lila.mon.TimerPath)(f: => A): A =
val timer = path(lila.mon).start()
def syncMon[A](path: Timer)(f: => A): A =
val timer = path.start()
val res = f
timer.stop()
res
+753
View File
@@ -0,0 +1,753 @@
package lila.mon
import com.github.benmanes.caffeine.cache.Cache as CaffeineCache
import kamon.metric.Timer
import kamon.tag.TagSet
import kamon.Kamon.{ timer, gauge, counter, histogram }
import chess.variant.Variant
import lila.core.id.*
import lila.core.net.*
import lila.core.userId.{ UserId, UserName }
import lila.core.perf.PerfKey
// https://github.com/kamon-io/Kamon/issues/752
extension (s: String)
def escape: String =
val builder = java.lang.StringBuilder(s.length)
for c <- s.toCharArray do
if c != '"' && c != '\n' && c != '\\'
then builder.append(c)
builder.toString
private def tags(elems: (String, Any)*): Map[String, Any] = Map.from(elems)
object http:
private val reqTime = timer("http.time")
private val reqCount = counter("http.count")
private val mobCount = counter("http.mobile.count")
def time(action: String) = reqTime.withTag("action", action)
def count(action: String, client: String, method: String, code: Int) =
reqCount.withTags:
tags("action" -> action, "client" -> client, "method" -> method, "code" -> code.toLong)
def errorCount(action: String, client: String, method: String, code: Int) =
counter("http.error").withTags:
tags("action" -> action, "client" -> client, "method" -> method, "code" -> code.toLong)
def mobileCount(action: String, version: String, auth: Boolean, os: String) =
mobCount.withTags:
tags(
"action" -> action,
"version" -> version,
"auth" -> (if auth then "auth" else "anon"),
"os" -> os
)
def path(p: String) = counter("http.path.count").withTag("path", p.escape)
val userGamesCost = counter("http.userGames.cost").withoutTags()
def csrfError(tpe: String, action: String, client: String) =
counter("http.csrf.error").withTags(tags("type" -> tpe, "action" -> action, "client" -> client))
val fingerPrint = timer("http.fingerPrint.time").withoutTags()
object syncache:
def miss(name: String) = counter("syncache.miss").withTag("name", name)
def timeout(name: String) = counter("syncache.timeout").withTag("name", name)
def compute(name: String) = timer("syncache.compute").withTag("name", name)
def wait(name: String) = timer("syncache.wait").withTag("name", name)
def caffeineStats(cache: CaffeineCache[?, ?], name: String): Unit =
val stats = cache.stats
gauge("caffeine.request").withTags(tags("name" -> name, "hit" -> true)).update(stats.hitCount.toDouble)
gauge("caffeine.request").withTags(tags("name" -> name, "hit" -> false)).update(stats.missCount.toDouble)
histogram("caffeine.hit.rate").withTag("name", name).record((stats.hitRate * 100000).toLong)
if stats.totalLoadTime > 0 then
gauge("caffeine.load.count")
.withTags(tags("name" -> name, "success" -> "success"))
.update(stats.loadSuccessCount.toDouble)
gauge("caffeine.load.count")
.withTags(tags("name" -> name, "success" -> "failure"))
.update(stats.loadFailureCount.toDouble)
gauge("caffeine.loadTime.cumulated")
.withTag("name", name)
.update(stats.totalLoadTime / 1000000d) // in millis; too much nanos for Kamon to handle)
timer("caffeine.loadTime.penalty").withTag("name", name).record(stats.averageLoadPenalty.toLong)
gauge("caffeine.eviction.count").withTag("name", name).update(stats.evictionCount.toDouble)
gauge("caffeine.entry.count").withTag("name", name).update(cache.estimatedSize.toDouble)
object mongoCache:
def request(name: String, hit: Boolean) =
counter("mongocache.request").withTags:
tags(
"name" -> name,
"hit" -> hit
)
def compute(name: String) = timer("mongocache.compute").withTag("name", name)
object evalCache:
private val r = counter("evalCache.request")
def request(ply: Int, isHit: Boolean) =
r.withTags(tags("ply" -> (if ply < 15 then ply.toString else "15+"), "hit" -> isHit))
object upgrade:
val count = counter("evalCache.upgrade.count").withoutTags()
val members = gauge("evalCache.upgrade.members").withoutTags()
val evals = gauge("evalCache.upgrade.evals").withoutTags()
val expirable = gauge("evalCache.upgrade.expirable").withoutTags()
object lobby:
object hook:
val create = counter("lobby.hook.create").withoutTags()
val join = counter("lobby.hook.join").withoutTags()
val size = histogram("lobby.hook.size").withoutTags()
def apiCreate(client: String) = counter("lobby.hook.apiCreate").withTag("client", client)
object seek:
val create = counter("lobby.seek.create").withoutTags()
val join = counter("lobby.seek.join").withoutTags()
object socket:
val getSris = timer("lobby.socket.getSris").withoutTags()
val member = gauge("lobby.socket.member").withoutTags()
val idle = gauge("lobby.socket.idle").withoutTags()
val hookSubscribers = gauge("lobby.socket.hookSubscribers").withoutTags()
object pool:
object wave:
def scheduled(id: String) = counter("lobby.pool.wave.scheduled").withTag("pool", id)
def full(id: String) = counter("lobby.pool.wave.full").withTag("pool", id)
def candidates(id: String) = histogram("lobby.pool.wave.candidates").withTag("pool", id)
def paired(id: String) = histogram("lobby.pool.wave.paired").withTag("pool", id)
def missed(id: String) = histogram("lobby.pool.wave.missed").withTag("pool", id)
def ratingDiff(id: String) = histogram("lobby.pool.wave.ratingDiff").withTag("pool", id)
def withRange(id: String) = histogram("lobby.pool.wave.withRange").withTag("pool", id)
object thieve:
def stolen(id: String) = histogram("lobby.pool.thieve.stolen").withTag("pool", id)
private val lobbySegment = timer("lobby.segment")
def segment(seg: String) = lobbySegment.withTag("segment", seg)
object rating:
def distribution(perfKey: PerfKey, rating: Int) =
gauge("rating.distribution").withTags(tags("perf" -> perfKey, "rating" -> rating.toLong))
object regulator:
def micropoints(perfKey: PerfKey) = histogram("rating.regulator").withTag("perf", perfKey.value)
object perfStat:
def indexTime = timer("perfStat.indexTime").withoutTags()
object round:
object api:
val player = timer("round.api").withTag("endpoint", "player")
val watcher = timer("round.api").withTag("endpoint", "watcher")
object forecast:
val create = counter("round.forecast.create").withoutTags()
object move:
object lag:
val compDeviation = histogram("round.move.lag.comp_deviation").withoutTags()
def uncomped(key: String) = histogram("round.move.lag.uncomped_ms").withTag("key", key)
def uncompStdDev(key: String) = histogram("round.move.lag.uncomp_stdev_ms").withTag("key", key)
val stdDev = histogram("round.move.lag.stddev_ms").withoutTags()
val mean = histogram("round.move.lag.mean_ms").withoutTags()
val coefVar = histogram("round.move.lag.coef_var_1000").withoutTags()
val compEstStdErr = histogram("round.move.lag.comp_est_stderr_1000").withoutTags()
val compEstOverErr = histogram("round.move.lag.avg_over_error_ms").withoutTags()
val moveComp = timer("round.move.lag.comped").withoutTags()
val time = timer("round.move.time").withoutTags()
object error:
val client = counter("round.error").withTag("from", "client")
val fishnet = counter("round.error").withTag("from", "fishnet")
val glicko = counter("round.error").withTag("from", "glicko")
val other = counter("round.error").withTag("from", "other")
object titivate:
val time = future("round.titivate.time")
val game = histogram("round.titivate.game").withoutTags() // how many games were processed
val total = histogram("round.titivate.total").withoutTags() // how many games should have been processed
val old = histogram("round.titivate.old").withoutTags() // how many old games remain
def broken(error: String) = counter("round.titivate.broken").withTag("error", error) // broken game
object alarm:
val time = timer("round.alarm.time").withoutTags()
object expiration:
val count = counter("round.expiration.count").withoutTags()
val asyncActorCount = gauge("round.asyncActor.count").withoutTags()
object correspondenceEmail:
val emails = histogram("round.correspondenceEmail.emails").withoutTags()
val time = future("round.correspondenceEmail.time")
object farming:
val bot = counter("round.farming.bot").withoutTags()
val provisional = counter("round.farming.provisional").withoutTags()
object playban:
def outcome(out: String) = counter("playban.outcome").withTag("outcome", out)
object ban:
val count = counter("playban.ban.count").withoutTags()
val mins = histogram("playban.ban.mins").withoutTags()
object explorer:
object index:
def count(success: Boolean) = counter("explorer.index.count").withTag("success", successTag(success))
val time = timer("explorer.index.time").withoutTags()
object timeline:
val notification = counter("timeline.notification").withoutTags()
object insight:
val user = future("insight.request.time", "user")
val peers = future("insight.request.time", "peer")
val index = future("insight.index.time")
object tutor:
def buildSegment(segment: String) = future("tutor.build.segment", segment)
val buildFull = future("tutor.build.full")
val askMine = askAs("mine")
val askPeer = askAs("peer")
val buildTimeout = counter("tutor.build.timeout").withoutTags()
def peerMatch(hit: Boolean, perf: PerfKey) = counter("tutor.peerMatch").withTags:
tags("hit" -> hitTag(hit), "perf" -> perf)
val parallelism = gauge("tutor.build.parallelism").withoutTags()
val fishnetMissing = histogram("tutor.fishnet.missing").withoutTags()
private def askAs(as: "mine" | "peer")(question: String, perf: PerfKey | "all") =
future("tutor.insight.ask", tags("question" -> question, "perf" -> perf, "as" -> as))
object search:
def time(op: "search" | "count", index: String, success: Boolean) =
timer("search.client.time").withTags:
tags(
"op" -> op,
"index" -> index,
"success" -> successTag(success)
)
object asyncActor:
def overflow(name: String) = counter("asyncActor.overflow").withTag("name", name)
def queueSize(name: String) = histogram("asyncActor.queueSize").withTag("name", name)
object irc:
object zulip:
def say(stream: String) = future("irc.zulip.say", tags("stream" -> stream.escape))
object user:
val online = gauge("user.online").withoutTags()
object register:
def count(
confirm: String,
ipSusp: Boolean,
fp: Boolean,
proxy: Option[String],
country: String,
client: String
) =
counter("user.register.count").withTags:
tags(
"confirm" -> confirm,
"ipSusp" -> ipSusp,
"fp" -> fp,
"proxy" -> proxy.getOrElse("no"),
"country" -> country.escape,
"client" -> client
)
def result(client: String, result: String) =
counter("user.register.result").withTags:
tags("client" -> client, "result" -> result)
def mustConfirmEmail(v: String) = counter("user.register.mustConfirmEmail").withTag("type", v)
def confirmEmailResult(success: Boolean) =
counter("user.register.confirmEmail").withTag("success", successTag(success))
def modConfirmEmail(by: "mod" | "worker", result: String) =
counter("user.register.modConfirmEmail").withTags:
tags("by" -> by, "result" -> result)
object auth:
val bcFullMigrate = counter("user.auth.bcFullMigrate").withoutTags()
val hashTime = timer("user.auth.hashTime").withoutTags()
def count(success: Boolean) = counter("user.auth.count").withTag("success", successTag(success))
def passwordResetRequest(s: String) = counter("user.auth.passwordResetRequest").withTag("type", s)
def passwordResetConfirm(s: String) = counter("user.auth.passwordResetConfirm").withTag("type", s)
def reopenRequest(s: String) = counter("user.auth.reopenRequest").withTag("type", s)
def reopenConfirm(s: String) = counter("user.auth.reopenConfirm").withTag("type", s)
object oauth:
def request(success: Boolean) = counter("user.oauth.request").withTags:
tags("success" -> successTag(success))
private val userSegment = timer("user.segment")
def segment(seg: String) = userSegment.withTag("segment", seg)
def leaderboardCompute = future("user.leaderboard.compute")
def weeklyStableRanking(perf: PerfKey) = future("user.weeklyStableRanking", perf.value)
object actor:
def queueSize(name: String) = gauge("trouper.queueSize").withTag("name", name)
object mod:
object report:
val highest = gauge("mod.report.highest").withoutTags()
val close = counter("mod.report.close").withoutTags()
def create(reason: String, score: Int) =
counter("mod.report.create").withTags:
tags("reason" -> reason, "score" -> score)
object automod:
val request = future("mod.report.automod.request")
def assessment(a: String) = counter("mod.report.automod.assessment").withTag("assessment", a)
val imageRequest = future("mod.report.automod.image.request")
def imageFlagged(v: Boolean) = counter("mod.report.automod.image.flagged").withTag("flagged", v)
object log:
val create = counter("mod.log.create").withoutTags()
object irwin:
val report = counter("mod.report.irwin.report").withoutTags()
val mark = counter("mod.report.irwin.mark").withoutTags()
def ownerReport(name: String) = counter("mod.irwin.ownerReport").withTag("name", name)
def streamEventType(name: String) = counter("mod.irwin.stream.eventType").withTag("name", name)
object kaladin:
def request(by: String) = counter("mod.kaladin.request").withTag("by", by)
def insufficientMoves(by: String) = counter("mod.kaladin.insufficientMoves").withTag("by", by)
def queue(priority: Int) = gauge("mod.kaladin.queue").withTag("priority", priority)
def error(errKind: String) = counter("mod.kaladin.error").withTag("error", errKind)
val activation = histogram("mod.report.kaladin.activation").withoutTags()
val report = counter("mod.report.kaladin.report").withoutTags()
val mark = counter("mod.report.kaladin.mark").withoutTags()
object comm:
def segment(seg: String) = timer("mod.comm.segmentLat").withTag("segment", seg)
def zoneSegment(name: String) = future("mod.zone.segment", name)
object relay:
private def by(official: Boolean) = if official then "official" else "user"
private def relay(official: Boolean, id: RelayTourId, slug: String) =
tags("by" -> by(official), "slug" -> s"$slug/$id")
def ongoing(official: Boolean) = gauge("relay.ongoing").withTag("by", by(official))
val crowdMonitor = gauge("relay.crowdMonitor").withoutTags()
def moves(official: Boolean, id: RelayTourId, slug: String) =
counter("relay.moves").withTags(relay(official, id, slug))
def fetchTime(official: Boolean, id: RelayTourId, slug: String) =
timer("relay.fetch.time").withTags(relay(official, id, slug))
def syncTime(official: Boolean, id: RelayTourId, slug: String) =
timer("relay.sync.time").withTags(relay(official, id, slug))
def httpGet(code: Int, host: String, etag: String, proxy: Option[String]) =
timer("relay.http.get").withTags:
tags(
"code" -> code.toLong,
"host" -> host.escape,
"etag" -> etag.escape,
"proxy" -> proxy.getOrElse("none")
)
val dedup = counter("relay.fetch.dedup").withoutTags()
def push(name: String, user: UserName, client: String)(games: Int, moves: Int, errors: Int) =
val histogramTags = tags("name" -> name.escape, "user" -> user, "client" -> client.escape)
val counterTags = tags("name" -> name.escape, "user" -> user)
histogram("relay.push.games").withTags(histogramTags).record(games)
histogram("relay.push.moves").withTags(histogramTags).record(moves)
histogram("relay.push.errors").withTags(histogramTags).record(errors)
counter("relay.push.games.nb").withTags(counterTags).increment(games)
counter("relay.push.moves.nb").withTags(counterTags).increment(moves)
object bot:
def moves(username: String) = counter("bot.moves").withTag("name", username)
def chats(username: String) = counter("bot.chats").withTag("name", username)
def gameStream(event: "start" | "stop") = counter("bot.gameStream").withTag("event", event)
object cheat:
def selfReport(wildName: String, auth: Boolean) =
val name = if wildName.startsWith("soc: ") then "soc" else wildName.takeWhile(' ' !=)
counter("cheat.selfReport").withTags(tags("name" -> name.escape, "auth" -> auth))
val holdAlert = counter("cheat.holdAlert").withoutTags()
def autoAnalysis(reason: String) = counter("cheat.autoAnalysis").withTag("reason", reason)
val autoMark = counter("cheat.autoMark.count").withoutTags()
val autoReport = counter("cheat.autoReport.count").withoutTags()
object email:
object send:
private val c = counter("email.send")
val resetPassword = c.withTag("type", "resetPassword")
val magicLink = c.withTag("type", "magicLink")
val reopen = c.withTag("type", "reopen")
val fix = c.withTag("type", "fix")
val change = c.withTag("type", "change")
val confirmation = c.withTag("type", "confirmation")
val welcome = c.withTag("type", "welcome")
def time(mailer: String) = future("email.send.time", tags("mailer" -> mailer))
val disposableDomain = gauge("email.disposableDomain").withoutTags()
object security:
val torNodes = gauge("security.tor.node").withoutTags()
object firewall:
val block = counter("security.firewall.block").withoutTags()
val ip = gauge("security.firewall.ip").withoutTags()
val prints = gauge("security.firewall.prints").withoutTags()
object proxy:
val request = future("security.proxy.time")
def result(r: Option[String]) = counter("security.proxy.result").withTag("result", r.getOrElse("none"))
def hit(prox: String, action: String) =
counter("security.proxy.hit").withTags(tags("proxy" -> prox, "action" -> action))
def rateLimit(key: String) = counter("security.rateLimit.count").withTag("key", key)
def concurrencyLimit(key: String) = counter("security.concurrencyLimit.count").withTag("key", key)
object dnsApi:
val mx = future("security.dnsApi.mx.time")
object verifyMailApi:
def fetch(success: Boolean, ok: Boolean) =
timer("verifyMail.fetch").withTags(tags("success" -> successTag(success), "ok" -> ok))
object mailcheckApi:
def fetch(success: Boolean, ok: Boolean) =
timer("mailcheck.fetch").withTags(tags("success" -> successTag(success), "ok" -> ok))
object turnstile:
def hit(client: String, action: String, result: String) =
counter("turnstile.hit").withTags(tags("client" -> client, "action" -> action, "result" -> result))
object pwned:
def get(res: Boolean) = timer("security.pwned.result").withTag("res", res)
object geoip:
val epoch = gauge("security.geoip.epoch").withoutTags()
val loadTime = gauge("security.geoip.loadTime").withoutTags()
object login:
def attempt(byEmail: Boolean, pwned: Boolean, result: Boolean) =
counter("security.login.attempt").withTags:
tags(
"by" -> (if byEmail then "email" else "name"),
"pwned" -> pwned,
"result" -> result
)
def proxy(tpe: String) = counter("security.login.proxy").withTag("proxy", tpe)
def secretScanning(tokenType: String, source: String, hit: Boolean) =
counter("security.githubSecretScanning.hit").withTags(
tags("type" -> tokenType, "source" -> source.escape, "hit" -> hit)
)
def userTrust(trust: Boolean, cause: String) =
counter("security.userTrust").withTags(tags("trust" -> trust, "cause" -> cause)).increment()
object shutup:
def analyzer = timer("shutup.analyzer.time").withoutTags()
object tv:
object selector:
def candidates(channel: String) = histogram("tv.selector.candidates").withTag("channel", channel)
def cheats(channel: String) = histogram("tv.selector.cheats").withTag("channel", channel)
def rating(channel: String) = histogram("tv.selector.rating").withTag("channel", channel)
object streamer:
def online = gauge("tv.streamer.count").withoutTags()
def present(n: String) = gauge("tv.streamer.present").withTag("name", n.escape)
object relation:
private val c = counter("relation.action")
val follow = c.withTag("type", "follow")
val unfollow = c.withTag("type", "unfollow")
val block = c.withTag("type", "block")
val unblock = c.withTag("type", "unblock")
object clas:
object student:
def create(teacher: UserId) = counter("clas.student.create").withTag("teacher", teacher)
def invite(teacher: UserId) = counter("clas.student.invite").withTag("teacher", teacher)
final class bloomFilter(name: String):
def count = gauge(s"clas.${name}.bloomFilter.count").withoutTags()
def fu = future(s"clas.${name}.bloomFilter.future")
object tournament:
object pairing:
val batchSize = histogram("tournament.pairing.batchSize").withoutTags()
val create = future("tournament.pairing.create")
val createRanking = timer("tournament.pairing.create.ranking").withoutTags()
val createPairings = timer("tournament.pairing.create.pairings").withoutTags()
val createPlayerMap = timer("tournament.pairing.create.playerMap").withoutTags()
val createInserts = timer("tournament.pairing.create.inserts").withoutTags()
val createFeature = timer("tournament.pairing.create.feature").withoutTags()
val createAutoPairing = timer("tournament.pairing.create.autoPairing").withoutTags()
val prep = future("tournament.pairing.prep")
val wmmatching = timer("tournament.pairing.wmmatching").withoutTags()
val created = gauge("tournament.count").withTag("type", "created")
val started = gauge("tournament.count").withTag("type", "started")
val waitingPlayers = histogram("tournament.waitingPlayers").withoutTags()
object startedOrganizer:
val tick = future("tournament.startedOrganizer.tick")
val waitingUsers = future("tournament.startedOrganizer.waitingUsers")
object createdOrganizer:
val tick = future("tournament.createdOrganizer.tick")
object lilaHttp:
val tick = future("tournament.lilaHttp.tick")
val fullSize = histogram("tournament.lilaHttp.fullSize").withoutTags()
val nbTours = gauge("tournament.lilaHttp.nbTours").withoutTags()
def apiShowPartial(partial: Boolean, client: String)(success: Boolean) =
timer("tournament.api.show").withTags:
tags(
"partial" -> partial,
"success" -> successTag(success),
"client" -> client
)
def withdrawableIds(reason: String) = future("tournament.withdrawableIds", reason)
def action(tourId: String, action: String) =
timer("tournament.api.action").withTags(tags("tourId" -> tourId, "action" -> action))
object notifier:
def tournaments = counter("tournament.notify.tournaments").withoutTags()
def players = counter("tournament.notify.players").withoutTags()
object featuring:
def forTeams(page: "index" | "homepage") = future("tournament.featuring.forTeams", page)
object swiss:
val tick = future("swiss.tick")
val bbpairing = timer("swiss.bbpairing").withoutTags()
val scoringGet = future("swiss.scoring.get")
val scoringRecompute = future("swiss.scoring.recompute")
val startRound = future("swiss.director.startRound")
def games(status: String) = histogram("swiss.ongoingGames").withTag("status", status)
val json = future("swiss.json")
object plan:
object paypalLegacy:
val amount = histogram("plan.amount").withTag("service", "paypal")
object paypalCheckout:
val amount = histogram("plan.amount").withTag("service", "paypalCheckout")
val fetchAccessToken = future("plan.paypal.accessToken")
val stripe = histogram("plan.amount").withTag("service", "stripe")
val goal = gauge("plan.goal").withoutTags()
val current = gauge("plan.current").withoutTags()
val percent = gauge("plan.percent").withoutTags()
def webhook(service: String, tpe: String) =
counter("plan.webhook").withTags(tags("service" -> service, "tpe" -> tpe))
def intent(service: String, currency: java.util.Currency, coverFees: Boolean) =
counter("plan.intent").withTags:
tags("service" -> service, "currency" -> currency.getCurrencyCode, "coverFees" -> coverFees)
object charge:
def first(service: String) = counter("plan.charge.first").withTag("service", service)
def countryCents(country: String, currency: java.util.Currency, service: String, gift: Boolean) =
histogram("plan.charge.country.cents").withTags:
tags(
"country" -> country.escape,
"currency" -> currency.getCurrencyCode,
"service" -> service,
"gift" -> gift
)
object forum:
object post:
val create = counter("forum.post.create").withoutTags()
object topic:
val view = counter("forum.topic.view").withoutTags()
def reaction(r: String) = counter("forum.reaction").withTag("reaction", r)
object msg:
def post(verdict: String, isNew: Boolean, multi: Boolean) = counter("msg.post").withTags(
tags("verdict" -> verdict, "isNew" -> isNew, "multi" -> multi)
)
val teamBulk = histogram("msg.bulk.team").withoutTags()
def clasBulk(clasId: ClasId) = histogram("msg.bulk.clas").withTag("id", clasId.value)
object puzzle:
object selector:
object user:
def time(categ: String) = timer("puzzle.selector.user.puzzle").withTag("categ", categ)
def retries(categ: String) = histogram("puzzle.selector.user.retries").withTag("categ", categ)
val vote = histogram("puzzle.selector.user.vote").withoutTags()
def tier(t: String, categ: String, difficulty: String) =
counter("puzzle.selector.user.tier").withTags:
tags("tier" -> t, "categ" -> categ, "difficulty" -> difficulty)
def batch(nb: Int) = timer("puzzle.selector.user.batch").withTag("nb", nb)
object anon:
val time = timer("puzzle.selector.anon.puzzle").withoutTags()
def batch(nb: Int) = timer("puzzle.selector.anon.batch").withTag("nb", nb)
val vote = histogram("puzzle.selector.anon.vote").withoutTags()
def nextPuzzleResult(result: String) =
timer("puzzle.selector.user.puzzleResult").withTag("result", result)
def nextPathFor(categ: String, requester: String) =
timer("puzzle.path.nextFor").withTags(tags("categ" -> categ, "requester" -> requester))
object batch:
object selector:
val count = counter("puzzle.batch.selector.count").withoutTags()
val time = timer("puzzle.batch.selector").withoutTags()
val solve = counter("puzzle.batch.solve").withoutTags()
object round:
def attempt(user: Boolean, theme: String, rated: Boolean) =
counter("puzzle.attempt.count").withTags(tags("user" -> user, "theme" -> theme, "rated" -> rated))
object vote:
def count(up: Boolean, win: Boolean) =
counter("puzzle.vote.count").withTags:
tags(
"up" -> up,
"win" -> win
)
def theme(key: String, up: Option[Boolean], win: Boolean) =
counter("puzzle.vote.theme").withTags:
tags(
"up" -> up.fold("cancel")(_.toString),
"theme" -> key,
"win" -> win
)
val future = lila.mon.future("puzzle.vote.future")
val crazyGlicko = counter("puzzle.crazyGlicko").withoutTags()
object storm:
object selector:
val time = future("storm.selector.time")
val sets = histogram("storm.selector.sets").withoutTags()
val count = histogram("storm.selector.count").withoutTags()
val rating = histogram("storm.selector.rating").withoutTags()
def ratingSlice(index: Int) = histogram("storm.selector.ratingSlice").withTag("index", index)
object run:
def score(auth: Boolean) = histogram("storm.run.score").withTag("auth", auth)
def sign(cause: String) = counter("storm.run.sign").withTag("cause", cause)
object racer:
private def tpe(lobby: Boolean) = if lobby then "lobby" else "friend"
def race(lobby: Boolean) = counter("racer.lobby.race").withTag("tpe", tpe(lobby))
def players(lobby: Boolean) =
histogram("racer.lobby.players").withTag("tpe", tpe(lobby))
def score(lobby: Boolean, auth: Boolean) =
histogram("racer.player.score").withTags:
tags(
"tpe" -> tpe(lobby),
"auth" -> auth
)
object streak:
object selector:
val time = timer("streak.selector.time").withoutTags()
val count = histogram("streak.selector.count").withoutTags()
val rating = histogram("streak.selector.rating").withoutTags()
def ratingSlice(index: Int) = histogram("streak.selector.ratingSlice").withTag("index", index)
object run:
def score(auth: String) = histogram("streak.run.score").withTag("auth", auth)
object game:
import chess.{ Speed, Rated, Status }
import lila.core.game.Source
def finish(variant: Variant, speed: Speed, source: Option[Source], mode: Rated, status: Status) =
counter("game.finish").withTags:
tags(
"variant" -> variant.key,
"speed" -> speed.key,
"source" -> source.fold("unknown")(_.name),
"mode" -> mode.name,
"status" -> status.name
)
val fetch = counter("game.fetch.count").withoutTags()
val loadClockHistory = counter("game.loadClockHistory.count").withoutTags()
object pgn:
def encode(format: String) = timer("game.pgn.encode").withTag("format", format)
def decode(format: String) = timer("game.pgn.decode").withTag("format", format)
val idCollision = counter("game.idCollision").withoutTags()
def idGenerator(collisions: Int) = timer("game.idGenerator").withTags(tags("collisions" -> collisions))
object streamByOauthOrigin:
def event(tpe: String) = counter("game.streamByOauthOrigin.event").withTag("type", tpe)
def users(sel: String) = gauge("game.streamByOauthOrigin.users").withTag("selector", sel)
def streams(ua: UserAgent) = gauge("game.streamByOauthOrigin.streams").withTag("ua", ua.value)
val bloomFP = counter("game.streamByOauthOrigin.bloomFP").withoutTags()
object chat:
private val msgCounter = counter("chat.message")
def message(parent: String, troll: Boolean) =
msgCounter.withTags(tags("parent" -> parent, "troll" -> troll))
def fetch(parent: String) = timer("chat.fetch").withTag("parent", parent)
object push:
object register:
def in(platform: String) = counter("push.register").withTag("platform", platform)
val out = counter("push.register.out").withoutTags()
object web:
def post = future("push.web.post")
object send:
private def send(tpe: String)(platform: String, success: Boolean, count: Int): Unit =
counter("push.send")
.withTags:
tags(
"type" -> tpe,
"platform" -> platform,
"success" -> successTag(success)
)
.increment(count)
()
val move = send("move")
val takeback = send("takeback")
val draw = send("draw")
val corresAlarm = send("corresAlarm")
val finish = send("finish")
val message = send("message")
val tourSoon = send("tourSoon")
val forumMention = send("forumMention")
val invitedStudy = send("invitedStudy")
val streamStart = send("streamStart")
val broadcastRound = send("broadcastRound")
object challenge:
val create = send("challengeCreate")
val accept = send("challengeAccept")
val googleTokenTime = timer("push.send.googleToken").withoutTags()
def firebaseStatus(project: String, typ: String, status: Int) =
counter("push.firebase.status").withTags(tags("status" -> status, "project" -> project, "type" -> typ))
object fishnet:
object client:
object result:
private val c = counter("fishnet.client.result")
private def apply(r: String)(client: UserId) =
c.withTags(tags("client" -> client, "result" -> r))
val success = apply("success")
val failure = apply("failure")
val timeout = apply("timeout")
val notFound = apply("notFound")
val notAcquired = apply("notAcquired")
val abort = apply("abort")
def status(enabled: Boolean) = gauge("fishnet.client.status").withTag("enabled", enabled)
def version(v: String) = gauge("fishnet.client.version").withTag("version", v.escape)
def queueTime(sender: "system" | "user") = timer("fishnet.queue.db").withTag("sender", sender)
val acquire = future("fishnet.acquire")
def work(typ: String, as: "system" | "user") =
gauge("fishnet.work").withTags(tags("type" -> typ, "for" -> as))
def oldest(as: "system" | "user") = gauge("fishnet.oldest").withTag("for", as)
object analysis:
object by:
def movetime(client: UserId) = histogram("fishnet.analysis.movetime").withTag("client", client)
def node(client: UserId) = histogram("fishnet.analysis.node").withTag("client", client)
def nps(client: UserId) = histogram("fishnet.analysis.nps").withTag("client", client)
def depth(client: UserId) = histogram("fishnet.analysis.depth").withTag("client", client)
def pvSize(client: UserId) = histogram("fishnet.analysis.pvSize").withTag("client", client)
def pv(client: UserId, isLong: Boolean) =
counter("fishnet.analysis.pvs").withTags(tags("client" -> client, "long" -> isLong))
def totalMeganode(client: UserId) =
counter("fishnet.analysis.total.meganode").withTag("client", client)
def totalSecond(client: UserId) =
counter("fishnet.analysis.total.second").withTag("client", client)
def requestCount(tpe: "game" | "study") = counter("fishnet.analysis.request").withTag("type", tpe)
val evalCacheHits = histogram("fishnet.analysis.evalCacheHits").withoutTags()
val skipPositionsGame = future("fishnet.analysis.skipPositions.game")
val skipPositionsStudy = future("fishnet.analysis.skipPositions.study")
object http:
def request(hit: Boolean) = counter("fishnet.http.acquire").withTag("hit", hit)
def move(level: Int) = counter("fishnet.move.time").withTag("level", level)
def openingBook(variant: Variant, hit: Boolean) =
timer("fishnet.opening.hit").withTags:
tags("variant" -> variant.key, "hit" -> hitTag(hit))
object opening:
def searchTime = timer("opening.search.time").withoutTags()
object explorer:
def stats = future("opening.explorer.stats")
object study:
object tree:
val read = timer("study.tree.read").withoutTags()
val write = timer("study.tree.write").withoutTags()
object sequencer:
val chapterTime = timer("study.sequencer.chapter.time").withoutTags()
object api:
val users = counter("api.cost").withTag("endpoint", "users")
val activity = counter("api.cost").withTag("endpoint", "activity")
object challenge:
object bulk:
def scheduleNb(by: UserId) = counter("api.challenge.bulk.schedule.nb").withTag("by", by)
def createNb(by: UserId) = counter("api.challenge.bulk.create.nb").withTag("by", by)
object `export`:
object png:
val game = counter("export.png").withTag("type", "game")
val puzzle = counter("export.png").withTag("type", "puzzle")
object bus:
val classifiers = gauge("bus.classifiers").withoutTags()
object blocking:
def time(name: String) = timer("blocking.time").withTag("name", name)
def timeout(name: String) = counter("blocking.timeout").withTag("name", name)
object workQueue:
def offerFail(name: String, result: String) =
counter("workQueue.offerFail").withTags:
tags("name" -> name, "result" -> result)
def timeout(name: String) = counter("workQueue.timeout").withTag("name", name)
class parallelQueue(name: String):
val parallelism = gauge("parallelQueue.parallelism").withTag("name", name)
val computeTimeout = counter("parallelQueue.buildTimeout").withTag("name", name)
object markdown:
val time = timer("markdown.time").withoutTags()
def pgnsFromText = future("markdown.pgnsFromText")
object ublog:
def create(user: UserId) = counter("ublog.create").withTag("user", user)
def view = counter("ublog.view").withoutTags()
object automod:
val request = future("ublog.automod.request")
def quality(q: String) = counter("ublog.automod.quality").withTag("quality", q)
def flagged(f: Boolean) = counter("ublog.automod.flagged").withTag("flagged", f)
object picfit:
def uploadTime(user: UserId) = future("picfit.upload.time", tags("user" -> user))
def uploadSize(user: UserId) = histogram("picfit.upload.size").withTag("user", user)
object fideSync:
val time = future("fide.sync.time")
val players = gauge("fide.sync.players").withoutTags()
val updated = gauge("fide.sync.updated").withoutTags()
object recap:
val games = future("recap.build.games.time")
val puzzles = future("recap.build.puzzles.time")
object jvm:
def threads() =
val perState = gauge("jvm.threads.group")
val total = gauge("jvm.threads.group.total")
for
group <- scalalib.Jvm.threadGroups()
_ = total.withTags(tags("name" -> group.name)).update(group.total)
(state, count) <- group.states
yield perState.withTags(tags("name" -> group.name, "state" -> state.toString)).update(count)
object prometheus:
val lines = gauge("prometheus.lines").withoutTags()
def chronoSync[A] = Chronometer.syncMon[A]
private def future(name: String) = (success: Boolean) => timer(name).withTag("success", successTag(success))
private def future(name: String, tags: Map[String, Any]) = (success: Boolean) =>
timer(name).withTags(tags + ("success" -> successTag(success)))
private def future(name: String, segment: String)(success: Boolean) =
timer(name).withTags:
tags("success" -> successTag(success), "segment" -> segment)
private def successTag(success: Boolean) = if success then "success" else "failure"
private def hitTag(hit: Boolean) = if hit then "hit" else "miss"
import scala.language.implicitConversions
private given Conversion[UserId, String] = _.value
private given Conversion[Map[String, Any], TagSet] = TagSet.from
+4
View File
@@ -0,0 +1,4 @@
package lila.mon
object extensions:
export Chronometer.futureExtension.*
@@ -11,6 +11,7 @@ import scala.util.{ Failure, Success, Try }
import lila.core.net.Crawler
import lila.core.config.Secret
import lila.mon.extensions.*
final private class OpeningExplorer(
ws: StandaloneWSClient,
@@ -47,7 +48,7 @@ final private class OpeningExplorer(
err => fufail(s"Couldn't parse $err"),
data => fuccess(data.some)
)
.monSuccess(_.opening.explorer.stats)
.monSuccess(lila.mon.opening.explorer.stats)
.map(Success(_))
.recover:
case e: Exception =>
+2 -2
View File
@@ -6,7 +6,7 @@ import scalalib.HeapSort.topN
import java.text.Normalizer
import lila.common.Chronometer
import lila.mon.Chronometer
import lila.memo.CacheApi
case class OpeningSearchResult(opening: Opening):
@@ -114,7 +114,7 @@ private object OpeningSearch:
private given Ordering[Match] = Ordering.by { case Match(_, score) => score }
def apply(str: String, max: Int): List[Opening] = Chronometer.syncMon(_.opening.searchTime):
def apply(str: String, max: Int): List[Opening] = Chronometer.syncMon(lila.mon.opening.searchTime):
val query = makeQuery(str)
index
.flatMap: entry =>
@@ -2,6 +2,7 @@ package lila.perfStat
import lila.rating.PerfType
import lila.rating.PerfType.GamePerf
import lila.mon.extensions.*
final class PerfStatIndexer(
gameRepo: lila.core.game.GameRepo,
@@ -12,7 +13,7 @@ final class PerfStatIndexer(
maxSize = Max(64),
timeout = 10.seconds,
name = "perfStatIndexer",
lila.log.asyncActorMonitor.full
lila.mon.asyncActorMonitor.full
)
private[perfStat] def userPerf(user: UserId, perf: GamePerf): Fu[PerfStat] =
@@ -30,7 +31,7 @@ final class PerfStatIndexer(
else perfStat
.flatMap: ps =>
storage.insert(ps).recover(lila.db.ignoreDuplicateKey).inject(ps)
.mon(_.perfStat.indexTime)
.mon(lila.mon.perfStat.indexTime)
def addGame(game: Game): Funit =
game.players.toList.sequentiallyVoid: player =>
+4 -3
View File
@@ -1,5 +1,7 @@
package lila.plan
import java.util.Currency
import play.api.ConfigLoader
import play.api.i18n.Lang
import play.api.libs.json.*
@@ -9,8 +11,7 @@ import play.api.libs.ws.JsonBodyReadables.*
import play.api.libs.ws.JsonBodyWritables.*
import play.api.libs.ws.{ StandaloneWSClient, StandaloneWSResponse, WSAuthScheme }
import java.util.Currency
import lila.mon.extensions.*
import lila.common.Json.given
import lila.common.autoconfig.*
import lila.common.config.given
@@ -236,7 +237,7 @@ final private class PayPalClient(
(res.body[JsValue] \ "access_token").validate[String] match
case JsError(err) => fufail(s"PayPal access token ${err} ${res.body[String].take(200)}")
case JsSuccess(token, _) => fuccess(AccessToken(token))
.monSuccess(_.plan.paypalCheckout.fetchAccessToken)
.monSuccess(lila.mon.plan.paypalCheckout.fetchAccessToken)
object PayPalClient:
+3 -1
View File
@@ -1,10 +1,12 @@
package lila.plan
import java.util.Currency
import play.api.data.*
import play.api.data.Forms.*
import play.api.data.validation.Constraints
import java.util.Currency
import lila.mon.extensions.*
case class PlanCheckout(
email: Option[String],
+1 -1
View File
@@ -18,7 +18,7 @@ final private class GameStarter(
maxSize = Max(64),
timeout = 10.seconds,
name = "gameStarter",
lila.log.asyncActorMonitor.full
lila.mon.asyncActorMonitor.full
)
def apply(pool: PoolConfig, couples: Vector[MatchMaking.Couple]): Funit =
+4 -4
View File
@@ -8,7 +8,7 @@ import play.api.libs.ws.StandaloneWSClient
import scalalib.cache.FrequencyThreshold
import scalalib.data.LazyFu
import lila.common.Chronometer
import lila.mon.extensions.*
final private class FirebasePush(
deviceApi: DeviceApi,
@@ -26,7 +26,7 @@ final private class FirebasePush(
maxSize = Max(512),
timeout = 10.seconds,
name = "firebasePush",
lila.log.asyncActorMonitor.full
lila.mon.asyncActorMonitor.full
)
def apply(userId: UserId, data: LazyFu[PushApi.Data]): Funit =
@@ -55,10 +55,10 @@ final private class FirebasePush(
// access token has 1h lifetime and is requested only if expired
token <- workQueue {
Future:
Chronometer.syncMon(_.blocking.time("firebase")):
lila.mon.Chronometer.syncMon(lila.mon.blocking.time("firebase")):
creds.refreshIfExpired()
creds.getAccessToken()
}.chronometer.mon(_.push.googleTokenTime).result
}.chronometer.mon(lila.mon.push.googleTokenTime).result
_ <- send(token, device, config, data)
yield ()
yield ()
+2 -1
View File
@@ -9,6 +9,7 @@ import scalalib.data.LazyFu
import lila.common.Json.given
import lila.common.autoconfig.*
import lila.mon.extensions.*
final private class WebPush(
webSubscriptionApi: WebSubscriptionApi,
@@ -77,7 +78,7 @@ final private class WebPush(
.map: n =>
logger.info(s"[push] web: $n/${staleEndpoints.size} stale endpoints unsubscribed")
case res => fufail(s"[push] web: ${res.status} ${res.body}")
.monSuccess(_.push.web.post)
.monSuccess(lila.mon.push.web.post)
private object WebPush:
+3 -2
View File
@@ -4,6 +4,7 @@ import scalalib.ThreadLocalRandom
import lila.db.dsl.*
import lila.memo.CacheApi
import lila.mon.extensions.*
final class PuzzleAnon(
colls: PuzzleColls,
@@ -18,7 +19,7 @@ final class PuzzleAnon(
pool
.get(angle -> diff)
.map(color.fold[Vector[Puzzle] => Option[Puzzle]](ThreadLocalRandom.oneOf)(selectWithColor))
.mon(_.puzzle.selector.anon.time)
.mon(lila.mon.puzzle.selector.anon.time)
.addEffect:
_.foreach: puzzle =>
lila.mon.puzzle.selector.anon.vote.record(100 + math.round(puzzle.vote * 100))
@@ -31,7 +32,7 @@ final class PuzzleAnon(
nextTry(1)
def getBatchFor(angle: PuzzleAngle, diff: PuzzleDifficulty, nb: Int): Fu[Vector[Puzzle]] =
pool.get(angle -> diff).map(_.take(nb)).mon(_.puzzle.selector.anon.batch(nb))
pool.get(angle -> diff).map(_.take(nb)).mon(lila.mon.puzzle.selector.anon.batch(nb))
private val poolSize = 150
+3 -2
View File
@@ -6,6 +6,7 @@ import scalalib.paginator.Paginator
import lila.core.i18n.I18nKey
import lila.db.dsl.{ *, given }
import lila.db.paginator.Adapter
import lila.mon.extensions.*
final class PuzzleApi(
colls: PuzzleColls,
@@ -66,7 +67,7 @@ final class PuzzleApi(
expiration = 1.minute,
timeout = 3.seconds,
name = "puzzle.vote",
monitor = lila.log.asyncActorMonitor.highCardinality
monitor = lila.mon.asyncActorMonitor.highCardinality
)
def update(id: PuzzleId, user: User, vote: Boolean): Funit =
@@ -86,7 +87,7 @@ final class PuzzleApi(
})
.void
}
.monSuccess(_.puzzle.vote.future)
.monSuccess(lila.mon.puzzle.vote.future)
.recoverDefault
private def updatePuzzle(puzzleId: PuzzleId, newVote: Int, prevVote: Option[Int]): Funit =
+2 -1
View File
@@ -1,6 +1,7 @@
package lila.puzzle
import lila.db.dsl.*
import lila.mon.extensions.*
// mobile app
final class PuzzleBatch(
@@ -55,4 +56,4 @@ final class PuzzleBatch(
)
.map:
_.view.flatMap(puzzleReader.readOpt).toVector
.mon(_.puzzle.selector.user.batch(nb = nb))
.mon(lila.mon.puzzle.selector.user.batch(nb = nb))
+2 -2
View File
@@ -25,7 +25,7 @@ final private[puzzle] class PuzzleFinisher(
expiration = 5.minutes,
timeout = 5.seconds,
name = "puzzle.finish",
lila.log.asyncActorMonitor.full
lila.mon.asyncActorMonitor.full
)
private val calculator = GlickoCalculator()
@@ -130,7 +130,7 @@ final private[puzzle] class PuzzleFinisher(
date = now
)
val userPerf = perf
.addOrReset(_.puzzle.crazyGlicko, s"puzzle ${puzzle.id}")(userGlicko, now)
.addOrReset(lila.mon.puzzle.crazyGlicko, s"puzzle ${puzzle.id}")(userGlicko, now)
.pipe: p =>
p.copy(glicko = ponder.player(angle, win, perf.glicko -> p.glicko, puzzle.glicko))
(round, newPuzzleGlicko, userPerf)
@@ -5,6 +5,7 @@ import reactivemongo.akkastream.cursorProducer
import lila.common.{ LilaOpeningFamily, LilaStream, SimpleOpening }
import lila.core.i18n.I18nKey
import lila.mon.extensions.*
import lila.db.dsl.{ *, given }
import lila.memo.{ CacheApi, MongoCache }
import lila.memo.CacheApi.buildAsyncTimeout
+2 -1
View File
@@ -3,6 +3,7 @@ package lila.puzzle
import scalalib.Iso
import lila.db.dsl.{ *, given }
import lila.mon.extensions.*
object PuzzlePath:
@@ -65,7 +66,7 @@ h":"5B7ADA38","planCacheKey":"7FF0C349","queryFramework":"classic","reslen":286,
nextFor(requester)(angle, actualTier, difficulty, previousPaths, compromise + 1)
case _ => fuccess(none)
}.mon:
_.puzzle.nextPathFor(angle.categ, requester)
lila.mon.puzzle.nextPathFor(angle.categ, requester)
def select(angle: PuzzleAngle, tier: PuzzleTier, rating: Range) = $doc(
"min".$lte(f"${angle.key}${sep}${tier}${sep}${rating.max}%04d"),
+3 -2
View File
@@ -1,6 +1,7 @@
package lila.puzzle
import lila.db.dsl.{ *, given }
import lila.mon.extensions.*
final class PuzzleSelector(
colls: PuzzleColls,
@@ -43,7 +44,7 @@ final class PuzzleSelector(
,
some
)
.mon(_.puzzle.selector.user.time(angle.categ))
.mon(lila.mon.puzzle.selector.user.time(angle.categ))
private def findNextPuzzleFor(angle: PuzzleAngle, retries: Int)(using me: Me, perf: Perf): Fu[Puzzle] =
sessionApi
@@ -140,4 +141,4 @@ final class PuzzleSelector(
PuzzleAlreadyPlayed(puzzle)
else PuzzleFound(puzzle)
.monValue: result =>
_.puzzle.selector.nextPuzzleResult(result.name)
lila.mon.puzzle.selector.nextPuzzleResult(result.name)
+2 -1
View File
@@ -3,6 +3,7 @@ package lila.puzzle
import lila.db.dsl.{ *, given }
import lila.memo.CacheApi
import lila.memo.CacheApi.buildAsyncTimeout
import lila.mon.extensions.*
case class PuzzleStreak(ids: String, first: Puzzle)
@@ -75,7 +76,7 @@ final class PuzzleStreakApi(colls: PuzzleColls, cacheApi: CacheApi)(using Execut
)
.map:
_.flatMap(puzzleReader.readOpt)
.mon(_.streak.selector.time)
.mon(lila.mon.streak.selector.time)
.addEffect(monitor)
.map: puzzles =>
puzzles.headOption.map:
@@ -5,6 +5,7 @@ import reactivemongo.akkastream.cursorProducer
import lila.common.LilaStream
import lila.db.dsl.{ *, given }
import lila.mon.extensions.*
final private class PuzzleTagger(colls: PuzzleColls, openingApi: PuzzleOpeningApi)(using
ec: Executor,
+1 -1
View File
@@ -52,7 +52,7 @@ final class RacerApi(
maxSize = Max(32),
timeout = 20.seconds,
name = "racer.rematch",
lila.log.asyncActorMonitor.full
lila.mon.asyncActorMonitor.full
)
def rematch(race: RacerRace, player: RacerPlayer.Id): Fu[RacerRace.Id] = race.rematch.flatMap(get) match
+1 -1
View File
@@ -16,7 +16,7 @@ final class RacerLobby(api: RacerApi)(using Executor)(using scheduler: Scheduler
maxSize = Max(128),
timeout = 20.seconds,
name = "racer.lobby",
lila.log.asyncActorMonitor.full
lila.mon.asyncActorMonitor.full
)
private val fallbackRace = RacerRace.make(RacerPlayer.lichess, Nil, 10)
+2 -2
View File
@@ -13,7 +13,7 @@ object PerfExt:
extension (p: Perf)
def addOrReset(monitor: lila.mon.CounterPath, msg: => String)(player: Glicko, date: Instant): Perf =
def addOrReset(counter: kamon.metric.Counter, msg: => String)(player: Glicko, date: Instant): Perf =
val newGlicko = player.copy(
rating = player.rating
.atMost(p.glicko.rating + lila.rating.Glicko.maxRatingDelta)
@@ -30,7 +30,7 @@ object PerfExt:
if newGlicko.sanityCheck then append(newGlicko)
else
lila.log("rating").error(s"Crazy Glicko2 $msg")
monitor(lila.mon).increment()
counter.increment()
append(lila.rating.Glicko.default)
def refund(points: Int): Perf =
+3 -2
View File
@@ -10,6 +10,7 @@ import lila.common.SimpleOpening
import lila.db.dsl.{ *, given }
import lila.game.Query
import lila.core.game.Source
import lila.mon.extensions.*
private final class RecapBuilder(
repo: RecapRepo,
@@ -51,7 +52,7 @@ private final class RecapBuilder(
nbs = NbWin(total = nb, win = wins - fixes),
votes = PuzzleVotes(nb = votes, themes = themes)
)
.monSuccess(_.recap.puzzles)
.monSuccess(lila.mon.recap.puzzles)
private def makeGameRecap(scan: GameScan): RecapGames =
RecapGames(
@@ -85,7 +86,7 @@ private final class RecapBuilder(
.sortedCursor(query, Query.sortChronological)
.documentSource()
.runFold(GameScan())(_.addGame(userId)(_))
.monSuccess(_.recap.games)
.monSuccess(lila.mon.recap.games)
private case class GameScan(
nbs: NbWin = NbWin(),
+2 -1
View File
@@ -6,6 +6,7 @@ import play.api.libs.ws.*
import play.shaded.ahc.org.asynchttpclient.util.HttpUtils.extractContentTypeCharsetAttribute
import lila.core.lilaism.LilaException
import lila.mon.extensions.*
/* Extra generic features for play WS client,
* without any knowledge of broadcast specifics.
@@ -73,7 +74,7 @@ private final class HttpClient(
req
.get()
.monValue: res =>
_.relay.httpGet(
lila.mon.relay.httpGet(
res.status,
url.host.toString,
etag = monitorEtagHit(req, res),
+3 -2
View File
@@ -13,6 +13,7 @@ import lila.game.{ GameRepo, PgnDump }
import lila.memo.CacheApi
import lila.relay.RelayRound.Sync
import lila.study.{ MultiPgn, StudyPgnImport }
import lila.mon.extensions.*
final private class RelayFetch(
sync: RelaySync,
@@ -83,7 +84,7 @@ final private class RelayFetch(
else
val syncFu = for
allGamesInSourceNoLimit <- fetchGames(rt).mon:
_.relay.fetchTime(rt.tour.official, rt.tour.id, rt.tour.slug)
lila.mon.relay.fetchTime(rt.tour.official, rt.tour.id, rt.tour.slug)
allGamesInSource = allGamesInSourceNoLimit.take(maxGamesToRead(rt.tour.official).value)
filtered = RelayGame.filter(rt.round.sync.onlyRound)(allGamesInSource)
sliced = RelayGame.Slices.filterAndOrder(~rt.round.sync.slices)(filtered)
@@ -98,7 +99,7 @@ final private class RelayFetch(
res <- sync
.updateStudyChapters(rt, reordered)
.withTimeoutError(7.seconds, SyncResult.Timeout)
.mon(_.relay.syncTime(rt.tour.official, rt.tour.id, rt.tour.slug))
.mon(lila.mon.relay.syncTime(rt.tour.official, rt.tour.id, rt.tour.slug))
games = res.plan.input.games
_ <- notifyAdmin.orphanBoards.inspectPlan(rt, res.plan)
nbGamesFinished = games.count(_.points.isDefined)
+1 -1
View File
@@ -26,7 +26,7 @@ final class RelayPush(
expiration = 1.minute,
timeout = 10.seconds,
name = "relay.push",
lila.log.asyncActorMonitor.full
lila.mon.asyncActorMonitor.full
)
def apply(rt: RelayRound.WithTour, pgn: PgnStr)(using Me, UserAgent): Fu[Results] =
+2 -1
View File
@@ -12,6 +12,7 @@ import lila.common.Json.given
import lila.core.config.Secret
import lila.core.data.Text
import lila.core.id.ImageId
import lila.mon.extensions.*
import lila.memo.{ ImageAutomod, ImageAutomodRequest, Dimensions }
import lila.memo.SettingStore.Text.given
@@ -128,7 +129,7 @@ final class Automod(
lila.mon.mod.report.automod.imageFlagged(flagged).increment()
flagged.option:
res.str("reason") | "No reason provided"
.monSuccess(_.mod.report.automod.imageRequest)
.monSuccess(lila.mon.mod.report.automod.imageRequest)
.recover:
case err =>
logger.error(err.getMessage, err)
+3 -2
View File
@@ -11,6 +11,7 @@ import lila.db.dsl.{ *, given }
import lila.memo.CacheApi.*
import lila.memo.SettingStore.Text.given
import lila.report.Room.Scores
import lila.mon.extensions.*
final class ReportApi(
val coll: Coll,
@@ -336,7 +337,7 @@ final class ReportApi(
systemPrompt = commsPromptSetting.get(),
model = commsModelSetting.get()
)
.monSuccess(_.mod.report.automod.request)
.monSuccess(lila.mon.mod.report.automod.request)
val candidate = for
(images, textResponse) <- automodApi.markdownImages(Markdown(userText)).zip(assessText)
flaggedImages = images.flatMap(_.automod).flatMap(_.flagged)
@@ -623,7 +624,7 @@ final class ReportApi(
maxSize = Max(32),
timeout = 20.seconds,
name = "report.inquiries",
lila.log.asyncActorMonitor.full
lila.mon.asyncActorMonitor.full
)
def allBySuspect: Fu[Map[UserId, Report.Inquiry]] =
+1
View File
@@ -8,6 +8,7 @@ import lila.common.Form.cleanNonEmptyText
import lila.core.LightUser
import lila.core.config.NetDomain
import lila.core.report.SuspectId
import lila.mon.extensions.*
final private[report] class ReportForm(lightUserAsync: LightUser.Getter)(using domain: NetDomain):
val cheatLinkConstraint: Constraint[ReportSetup] = Constraint("constraints.cheatgamelink"): setup =>
+2 -1
View File
@@ -7,6 +7,7 @@ import reactivemongo.api.bson.*
import lila.common.{ Bus, LilaScheduler, LilaStream }
import lila.core.user.LightUserApi
import lila.db.dsl.{ *, given }
import lila.mon.extensions.*
final private class CorresAlarm(
coll: Coll,
@@ -71,5 +72,5 @@ final private class CorresAlarm(
case (alarm, None) => deleteAlarm(alarm._id)
.toMat(LilaStream.sinkCount)(Keep.right)
.run()
.mon(_.round.alarm.time)
.mon(lila.mon.round.alarm.time)
.void
@@ -9,6 +9,7 @@ import lila.core.misc.mailer.*
import lila.core.notify.NotifyApi
import lila.db.dsl.{ *, given }
import lila.user.UserRepo
import lila.mon.extensions.*
final private class CorrespondenceEmail(gameRepo: GameRepo, userRepo: UserRepo, notifyApi: NotifyApi)(using
Executor,
@@ -26,7 +27,7 @@ final private class CorrespondenceEmail(gameRepo: GameRepo, userRepo: UserRepo,
.map { Bus.pub(_) }
.runWith(LilaStream.sinkCount)
.addEffect(lila.mon.round.correspondenceEmail.emails.record(_))
.monSuccess(_.round.correspondenceEmail.time)
.monSuccess(lila.mon.round.correspondenceEmail.time)
private def opponentStream =
notifyApi.prefColl
+1 -1
View File
@@ -80,7 +80,7 @@ final class PerfsUpdater(
val newPerfs = perfs
.focusKey(perfKey)
.modify:
_.addOrReset(_.round.error.glicko, s"game ${game.id}")(player, game.movedAt)
_.addOrReset(lila.mon.round.error.glicko, s"game ${game.id}")(player, game.movedAt)
if game.ratingVariant.standard
then updateStandard(newPerfs)
else newPerfs
+2 -1
View File
@@ -11,6 +11,7 @@ import lila.game.GameExt.*
import lila.game.{ Event, GameRepo, Player as GamePlayer, Progress }
import lila.room.RoomSocket.{ Protocol as RP, * }
import lila.round.RoundGame.*
import lila.mon.extensions.*
final private class RoundAsyncActor(
dependencies: RoundAsyncActor.Dependencies,
@@ -195,7 +196,7 @@ final private class RoundAsyncActor(
case RoundBus.FishnetPlay(uci, hash) =>
handle: game =>
player.fishnet(game, hash, uci)
.mon(_.round.move.time)
.mon(lila.mon.round.move.time)
case RoundBus.Abort(playerId) =>
handle(playerId): pov =>
+1
View File
@@ -15,6 +15,7 @@ import lila.core.net.IpAddress
import lila.core.round.*
import lila.core.socket.{ protocol as P, * }
import lila.room.RoomSocket.{ Protocol as RP, * }
import lila.mon.extensions.*
final class RoundSocket(
socketKit: ParallelSocketKit,
+2 -1
View File
@@ -8,6 +8,7 @@ import lila.core.round.{ Abandon, RoundBus }
import lila.db.dsl.*
import lila.game.GameExt.abandoned
import lila.game.{ GameRepo, Query }
import lila.mon.extensions.*
/*
* Cleans up unfinished games
@@ -54,7 +55,7 @@ final private class Titivate(
yield lila.mon.round.titivate.old.record(old)
run
.monSuccess(_.round.titivate.time)
.monSuccess(lila.mon.round.titivate.time)
.logFailure(logBranch)
.addEffectAnyway(scheduleNext())
@@ -2,6 +2,7 @@ package lila.search
import lila.search.client.SearchClient
import lila.search.spec.*
import lila.mon.extensions.*
class LilaSearchClient(client: SearchClient, cacheApi: lila.memo.CacheApi)(using Executor)
extends SearchClient:
@@ -29,7 +30,7 @@ class LilaSearchClient(client: SearchClient, cacheApi: lila.memo.CacheApi)(using
SearchOutput(Nil)
private def monitor[A](op: "search" | "count", index: String)(f: Fu[A]) =
f.monTry(res => _.search.time(op, index, res.isSuccess))
f.monTry(res => lila.mon.search.time(op, index, res.isSuccess))
extension (query: Query)
def index: String = query match
+2 -1
View File
@@ -7,6 +7,7 @@ import play.api.libs.ws.StandaloneWSClient
import lila.core.lilaism.LilaException
import lila.core.net.Domain
import lila.db.dsl.given
import lila.mon.extensions.*
final private class DnsApi(
ws: StandaloneWSClient,
@@ -36,7 +37,7 @@ final private class DnsApi(
}
.flatten
}
}.monSuccess(_.security.dnsApi.mx)
}.monSuccess(lila.mon.security.dnsApi.mx)
}
private def fetch[A](domain: Domain.Lower, tpe: String)(f: List[JsObject] => A): Fu[A] =
@@ -5,6 +5,7 @@ import play.api.data.validation.*
import lila.core.net.Domain
import lila.user.{ User, UserRepo }
import lila.core.email.NormalizedEmailAddress
import lila.mon.extensions.*
/** Validate and normalize emails
*/
+1 -1
View File
@@ -58,7 +58,7 @@ final class Env(
lazy val passwordHasher = PasswordHasher(
secret = config.passwordBPassSecret,
logRounds = 10,
hashTimer = lila.common.Chronometer.syncMon(_.user.auth.hashTime)
hashTimer = lila.mon.Chronometer.syncMon(lila.mon.user.auth.hashTime)
)
lazy val authenticator = wire[Authenticator]
+1 -1
View File
@@ -19,7 +19,7 @@ final class GeoIP(config: GeoIP.Config, scheduler: Scheduler)(using Executor):
private def loadFromFile(): Unit =
if config.file.nonEmpty then
try
val time = lila.common.Chronometer.sync:
val time = lila.mon.Chronometer.sync:
reader = DatabaseReader.Builder(java.io.File(config.file)).fileMode(FileMode.MEMORY).build.some
reader.foreach: r =>
val meta = r.getMetadata
+2 -1
View File
@@ -8,6 +8,7 @@ import play.api.libs.ws.StandaloneWSClient
import lila.core.net.IpAddress
import lila.core.security.{ Ip2ProxyApi, IsProxy }
import lila.common.HTTPRequest
import lila.mon.extensions.*
final class Ip2ProxySkip extends Ip2ProxyApi:
def ofReq(req: RequestHeader): Fu[IsProxy] = fuccess(IsProxy.empty)
@@ -89,7 +90,7 @@ final class Ip2ProxyServer(
.withTimeout(200.millis, "Ip2Proxy.fetch")
.dmap(_.body[JsValue])
.dmap(readProxyName)
.monSuccess(_.security.proxy.request)
.monSuccess(lila.mon.security.proxy.request)
.addEffect: result =>
lila.mon.security.proxy.result(result.name).increment()
.recoverDefault(IsProxy.empty)
+3 -1
View File
@@ -4,6 +4,8 @@ import com.roundeights.hasher.Implicits.*
import play.api.libs.ws.DefaultBodyReadables.*
import play.api.libs.ws.StandaloneWSClient
import lila.mon.extensions.*
opaque type IsPwned = Boolean
object IsPwned extends YesNo[IsPwned]
@@ -24,5 +26,5 @@ final class PwnedApi(ws: StandaloneWSClient, rangeUrl: String)(using Executor):
logger.warn(s"Pwnd ${url} ${res.status} ${res.body[String].take(200)}")
IsPwned(false)
.monValue: result =>
_.security.pwned.get(result.yes)
lila.mon.security.pwned.get(result.yes)
.recoverDefault(IsPwned(false))
@@ -11,6 +11,7 @@ import lila.core.security.ClearPassword
import lila.user.TotpSecret.{ base32, verify }
import lila.user.{ TotpSecret, TotpToken }
import lila.oauth.OAuthSignedClient.SimpleSignup
import lila.mon.extensions.*
final class SecurityForm(
userRepo: lila.user.UserRepo,
+3 -2
View File
@@ -6,6 +6,7 @@ import play.api.libs.ws.JsonBodyReadables.*
import play.api.libs.ws.StandaloneWSClient
import lila.core.net.Domain
import lila.mon.extensions.*
/* An expensive API detecting disposable email.
* Only hit after trying everything else (DnsApi)
@@ -77,7 +78,7 @@ final private class VerifyMail(
ok
).getOrElse:
throw lila.core.lilaism.LilaException(s"$url ${res.status} ${res.body[String].take(200)}")
.monTry(res => _.security.mailcheckApi.fetch(res.isSuccess, res.getOrElse(true)))
.monTry(res => lila.mon.security.mailcheckApi.fetch(res.isSuccess, res.getOrElse(true)))
private def fetchPaid(domain: Domain.Lower): Fu[Boolean] =
val url = s"https://verifymail.io/api/$domain"
@@ -99,4 +100,4 @@ final private class VerifyMail(
ok
).getOrElse:
throw lila.core.lilaism.LilaException(s"$url ${res.status} ${res.body[String].take(200)}")
.monTry(res => _.security.verifyMailApi.fetch(res.isSuccess, res.getOrElse(true)))
.monTry(res => lila.mon.security.verifyMailApi.fetch(res.isSuccess, res.getOrElse(true)))
+2 -2
View File
@@ -4,14 +4,14 @@ import lila.common.constants.bannedYoutubeIds
object Analyser extends lila.core.shutup.TextAnalyser:
def apply(raw: String): TextAnalysis = lila.common.Chronometer
def apply(raw: String): TextAnalysis = lila.mon.Chronometer
.sync:
val lower = raw.take(2000).toLowerCase
val processable = removeDiacriticalCombination(removeSlash(lower))
val matches = latinBigRegex.findAllMatchIn(latinify(processable)).toList :::
ruBigRegex.findAllMatchIn(lower).toList
TextAnalysis(lower, matches.map(_.toString))
.mon(_.shutup.analyzer)
.mon(lila.mon.shutup.analyzer)
.logIfSlow(100, logger)(_ => s"Slow shutup analyser ${raw.take(400)}")
.result
+1 -1
View File
@@ -37,7 +37,7 @@ final class SimulApi(
expiration = 10.minutes,
timeout = 10.seconds,
name = "simulApi",
lila.log.asyncActorMonitor.full
lila.mon.asyncActorMonitor.full
)
export repo.{ find, byIds }
+3 -2
View File
@@ -7,6 +7,7 @@ import lila.db.dsl.{ *, given }
import lila.memo.CacheApi
import lila.puzzle.PuzzleColls
import lila.common.BatchProvider
import lila.mon.extensions.*
/* The difficulty of storm should remain constant!
* Be very careful when adjusting the selector.
@@ -46,7 +47,7 @@ final class StormSelector(colls: PuzzleColls, cacheApi: CacheApi)(using Executor
private val setSize = ratingBuckets._2F.sum // 137
private val batchProvider =
BatchProvider[PuzzleSet]("stormSelector", timeout = 15.seconds): () =>
BatchProvider[PuzzleSet]("stormSelector", timeout = 15.seconds, lila.mon.asyncActorMonitor.full): () =>
aggregateMultipleSets:
if lila.common.Uptime.startedSinceMinutes(2) then setsPerAggregation else 1
@@ -92,7 +93,7 @@ final class StormSelector(colls: PuzzleColls, cacheApi: CacheApi)(using Executor
logger.warn:
s"selector: $setSize x $nbSets. ${docs.size} docs, ${puzzles.size} puzzles, ${setsToMake} sets, ${groups.size} groups: $showGroups"
.logTimeIfGt(s"storm selector x$nbSets", 8.seconds)
.monSuccess(_.storm.selector.time)
.monSuccess(lila.mon.storm.selector.time)
.recoverWith:
case e: IllegalArgumentException if nbSets > 1 =>
val retryNbSets = nbSets / 2
+5 -5
View File
@@ -2,7 +2,7 @@ package lila.study
import chess.format.UciPath
import lila.common.Chronometer
import lila.mon.Chronometer.syncMon
import lila.db.dsl.*
import lila.tree.{ Branch, Branches, NewBranch, NewRoot, NewTree, Root }
@@ -23,7 +23,7 @@ private object StudyFlatTree:
object reader:
def rootChildren(flatTree: Bdoc): Branches =
Chronometer.syncMon(_.study.tree.read):
syncMon(lila.mon.study.tree.read):
traverse:
flatTree.elements.toList
.collect:
@@ -32,7 +32,7 @@ private object StudyFlatTree:
.sortBy(-_.depth)
def newRoot(flatTree: Bdoc): Option[NewTree] =
Chronometer.syncMon(_.study.tree.read):
syncMon(lila.mon.study.tree.read):
traverseN:
flatTree.elements.toList
.collect:
@@ -72,11 +72,11 @@ private object StudyFlatTree:
object writer:
def rootChildren(root: Root): List[(String, Bdoc)] =
Chronometer.syncMon(_.study.tree.write):
syncMon(lila.mon.study.tree.write):
root.children.toList.flatMap { traverse(_, UciPath.root) }
def newRootChildren(root: NewRoot): List[(String, Bdoc)] =
Chronometer.syncMon(_.study.tree.write):
syncMon(lila.mon.study.tree.write):
root.tree.so:
_.mapAccuml_(UciPath.root)((acc, branch) =>
val path = acc + branch.id
+1 -1
View File
@@ -13,7 +13,7 @@ final private class StudySequencer(
expiration = 1.minute,
timeout = 10.seconds,
name = "study",
lila.log.asyncActorMonitor.highCardinality
lila.mon.asyncActorMonitor.highCardinality
)
def sequenceStudy[A <: Matchable: Zero](studyId: StudyId)(f: Study => Fu[A]): Fu[A] =
+1 -1
View File
@@ -117,7 +117,7 @@ final class StudyTopicApi(topicRepo: StudyTopicRepo, userTopicRepo: StudyUserTop
maxSize = Max(1),
timeout = 61.seconds,
name = "studyTopicAggregation",
lila.log.asyncActorMonitor.unhandled
lila.mon.asyncActorMonitor.unhandled
)
private[study] def recompute(): Unit =
+1 -1
View File
@@ -27,7 +27,7 @@ final private class PairingSystem(trf: SwissTrf, executable: String)(using
val command = s"$executable --$flavour $file -p"
val stdout = new collection.mutable.ListBuffer[String]
val stderr = new StringBuilder
val status = lila.common.Chronometer.syncMon(_.swiss.bbpairing):
val status = lila.mon.Chronometer.syncMon(lila.mon.swiss.bbpairing):
blocking:
command ! ProcessLogger(stdout append _, stderr append _)
if status != 0 then
+3 -2
View File
@@ -14,6 +14,7 @@ import lila.core.LightUser
import lila.core.round.RoundBus
import lila.core.swiss.{ IdName, SwissFinish }
import lila.core.userId.UserSearch
import lila.mon.extensions.*
import lila.db.dsl.{ *, given }
import lila.gathering.Condition.WithVerdicts
import lila.gathering.GreatPlayer
@@ -41,7 +42,7 @@ final class SwissApi(
expiration = 20.minutes,
timeout = 10.seconds,
name = "swiss.api",
lila.log.asyncActorMonitor.full
lila.mon.asyncActorMonitor.full
)
import BsonHandlers.{ *, given }
@@ -607,7 +608,7 @@ final class SwissApi(
)
yield cache.swissCache.clear(swiss.id)
} >> recomputeAndUpdateAll(id)
.monSuccess(_.swiss.tick)
.monSuccess(lila.mon.swiss.tick)
private def countPresentPlayers(swiss: Swiss) = SwissPlayer.fields: f =>
mongo.player.countSel($doc(f.swissId -> swiss.id, f.absent.$ne(true)))

Some files were not shown because too many files have changed in this diff Show More