001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.rest.server.management; 018 019import java.util.*; 020 021import org.apache.juneau.http.*; 022import org.apache.juneau.http.response.*; 023import org.apache.juneau.rest.server.*; 024import org.apache.juneau.rest.server.servlet.*; 025 026/** 027 * Child-resource flavor of the {@code /loggers} runtime log-level management endpoint. 028 * 029 * <p> 030 * Mounts as a routed child via {@link Rest#children() @Rest(children=LoggersResource.class)} under the 031 * subtree {@code /loggers} and delegates to a shared {@link LoggersManager} worker — the same logic 032 * the {@link LoggersMixin mixin} flavor uses, so the two forms cannot drift. Extends 033 * {@link BasicRestResource} so the returned beans serialize via the {@code BasicUniversalConfig} set 034 * (mirrors {@code HealthResource}). 035 * 036 * <p> 037 * As with the mixin flavor, the {@code GET} endpoints carry no built-in guard of their own — mount this 038 * resource only under a subtree that's already covered by whatever authentication/authorization the app applies 039 * to its management endpoints generally. See {@link LoggersMixin} for details. 040 * 041 * <h5 class='section'>Backend:</h5><ul> 042 * <li>Drives {@link JulLogBackend java.util.logging} by default; declare a Logback / Log4j2 backend 043 * explicitly via {@link LoggersSettings.Builder#backend(LogBackend)} — see {@link LogBackend}. 044 * </ul> 045 * 046 * <h5 class='section'>See Also:</h5><ul> 047 * <li class='jc'>{@link LoggersMixin} 048 * <li class='jc'>{@link LoggersManager} 049 * <li class='jc'>{@link LogBackend} 050 * </ul> 051 * 052 * @since 10.0.0 053 */ 054@Rest(path="/loggers") 055public class LoggersResource extends BasicRestResource { 056 057 private final LoggersManager manager = new LoggersManager(); 058 059 /** 060 * [GET /] - All loggers and their configured levels. 061 * 062 * @return A sorted map of logger name to configured level (empty string = inherited). 063 */ 064 @RestGet( 065 path="/*", 066 summary="Runtime logger levels", 067 description="Lists all loggers and their configured levels (empty = inherited from ancestor)." 068 ) 069 public Map<String,String> getLoggers(RestRequest req) { 070 return manager.getLevels(req.getContext()); 071 } 072 073 /** 074 * [GET /{name}] - One logger's configured level. 075 * 076 * @param req The HTTP request. 077 * @param name The logger name ("ROOT" for the root logger). 078 * @return The configured level name, empty string if inherited. 079 * @throws NotFound If no logger with that name is registered. 080 */ 081 @RestGet( 082 path="/{name}", 083 summary="Runtime logger level", 084 description="Returns the configured level of a single logger." 085 ) 086 public String getLogger(RestRequest req, @Path("name") String name) { 087 var level = manager.getLevel(req.getContext(), name); 088 if (level == null) 089 throw new NotFound("No logger named '%s' is registered.", name); 090 return level; 091 } 092 093 /** 094 * [PUT /{name}] - Set a logger's level at runtime. 095 * 096 * <p> 097 * <b>Sensitive (mutating) — deny-by-default:</b> responds {@code 403} unless a {@link LoggersSettings} 098 * bean with writes enabled is registered. 099 * 100 * @param req The HTTP request. 101 * @param name The logger name ("ROOT" for the root logger). 102 * @param level The level name (e.g. FINE, INFO, OFF), or blank to inherit from the ancestor. 103 * @return The newly-configured level (empty string if inherited). 104 * @throws Forbidden If the set-level endpoint is not enabled. 105 */ 106 @RestPut( 107 path="/{name}", 108 summary="Set logger level", 109 description="Sets the java.util.logging level of a single logger at runtime (blank to inherit). Disabled by default; opt in via LoggersSettings." 110 ) 111 public String setLogger(RestRequest req, @Path("name") String name, @Content String level) { 112 if (! manager.resolveSettings(req.getContext()).isWriteEnabled()) 113 throw new Forbidden("The /loggers set-level endpoint is disabled. Register a LoggersSettings bean with write enabled to use it."); 114 manager.setLevel(req.getContext(), name, level); 115 // setLevel creates the logger on demand, so getLevel always returns non-null here (level name or "" if inherited). 116 return manager.getLevel(req.getContext(), name); 117 } 118 119 /** 120 * [POST /{name}] - Set a logger's level at runtime (POST alias of {@link #setLogger(RestRequest,String,String)}). 121 * 122 * @param req The HTTP request. 123 * @param name The logger name ("ROOT" for the root logger). 124 * @param level The level name, or blank to inherit. 125 * @return The newly-configured level (empty string if inherited). 126 * @throws Forbidden If the set-level endpoint is not enabled. 127 */ 128 @RestPost( 129 path="/{name}", 130 summary="Set logger level", 131 description="Sets the java.util.logging level of a single logger at runtime (blank to inherit). Disabled by default; opt in via LoggersSettings." 132 ) 133 public String setLoggerPost(RestRequest req, @Path("name") String name, @Content String level) { 134 return setLogger(req, name, level); 135 } 136}