Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Enable runtime profiling |
---|---|
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA3-256: |
c2743a97b5339ec1991b616b1e3f69f4 |
User & Date: | stern 2024-09-26 12:52:19 |
Context
2024-09-26
| ||
16:47 | Reread zettel id mapping zettel when updated ... (check-in: 0073087bde user: stern tags: trunk) | |
12:52 | Enable runtime profiling ... (check-in: c2743a97b5 user: stern tags: trunk) | |
11:48 | Refactor kernel to remove some warnings ... (check-in: 105f0b6440 user: stern tags: trunk) | |
Changes
Changes to cmd/main.go.
︙ | ︙ | |||
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 | } } const ( keyAdminPort = "admin-port" keyAssetDir = "asset-dir" keyBaseURL = "base-url" keyDebug = "debug-mode" keyDefaultDirBoxType = "default-dir-box-type" keyInsecureCookie = "insecure-cookie" keyInsecureHTML = "insecure-html" keyListenAddr = "listen-addr" keyLogLevel = "log-level" keyMaxRequestSize = "max-request-size" keyOwner = "owner" keyPersistentCookie = "persistent-cookie" | > < > | 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 | } } const ( keyAdminPort = "admin-port" keyAssetDir = "asset-dir" keyBaseURL = "base-url" keyBoxOneURI = kernel.BoxURIs + "1" keyDebug = "debug-mode" keyDefaultDirBoxType = "default-dir-box-type" keyInsecureCookie = "insecure-cookie" keyInsecureHTML = "insecure-html" keyListenAddr = "listen-addr" keyLogLevel = "log-level" keyMaxRequestSize = "max-request-size" keyOwner = "owner" keyPersistentCookie = "persistent-cookie" keyReadOnly = "read-only-mode" keyRuntimeProfiling = "runtime-profiling" keyTokenLifetimeHTML = "token-lifetime-html" keyTokenLifetimeAPI = "token-lifetime-api" keyURLPrefix = "url-prefix" keyVerbose = "verbose-mode" ) func setServiceConfig(cfg *meta.Meta) bool { |
︙ | ︙ | |||
205 206 207 208 209 210 211 | val, found := cfg.Get(key) if !found { break } err = setConfigValue(err, kernel.BoxService, key, val) } | > | > | > | 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 | val, found := cfg.Get(key) if !found { break } err = setConfigValue(err, kernel.BoxService, key, val) } err = setConfigValue( err, kernel.ConfigService, kernel.ConfigInsecureHTML, cfg.GetDefault(keyInsecureHTML, kernel.ConfigSecureHTML)) err = setConfigValue( err, kernel.WebService, kernel.WebListenAddress, cfg.GetDefault(keyListenAddr, "127.0.0.1:23123")) if val, found := cfg.Get(keyBaseURL); found { err = setConfigValue(err, kernel.WebService, kernel.WebBaseURL, val) } if val, found := cfg.Get(keyURLPrefix); found { err = setConfigValue(err, kernel.WebService, kernel.WebURLPrefix, val) } err = setConfigValue(err, kernel.WebService, kernel.WebSecureCookie, !cfg.GetBool(keyInsecureCookie)) err = setConfigValue(err, kernel.WebService, kernel.WebPersistentCookie, cfg.GetBool(keyPersistentCookie)) if val, found := cfg.Get(keyMaxRequestSize); found { err = setConfigValue(err, kernel.WebService, kernel.WebMaxRequestSize, val) } err = setConfigValue( err, kernel.WebService, kernel.WebTokenLifetimeAPI, cfg.GetDefault(keyTokenLifetimeAPI, "")) err = setConfigValue( err, kernel.WebService, kernel.WebTokenLifetimeHTML, cfg.GetDefault(keyTokenLifetimeHTML, "")) err = setConfigValue(err, kernel.WebService, kernel.WebProfiling, debugMode || cfg.GetBool(keyRuntimeProfiling)) if val, found := cfg.Get(keyAssetDir); found { err = setConfigValue(err, kernel.WebService, kernel.WebAssetDir, val) } return err == nil } func setConfigValue(err error, subsys kernel.Service, key string, val any) error { |
︙ | ︙ |
Changes to docs/manual/00001004010000.zettel.
1 2 3 4 5 6 | id: 00001004010000 title: Zettelstore startup configuration role: manual tags: #configuration #manual #zettelstore syntax: zmk created: 20210126175322 | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | id: 00001004010000 title: Zettelstore startup configuration role: manual tags: #configuration #manual #zettelstore syntax: zmk created: 20210126175322 modified: 20240926144803 The configuration file, specified by the ''-c CONFIGFILE'' [[command line option|00001004051000]], allows you to specify some startup options. These cannot be stored in a [[configuration zettel|00001004020000]] because they are needed before Zettelstore can start or because of security reasons. For example, Zettelstore needs to know in advance on which network address it must listen or where zettel are stored. An attacker that is able to change the owner can do anything. Therefore, only the owner of the computer on which Zettelstore runs can change this information. |
︙ | ︙ | |||
46 47 48 49 50 51 52 | : Specifies a [[box|00001004011200]] where zettel are stored. During startup, __X__ is incremented, starting with one, until no key is found. This allows to configuring than one box. If no ''box-uri-1'' key is given, the overall effect will be the same as if only ''box-uri-1'' was specified with the value ""dir://.zettel"". In this case, even a key ''box-uri-2'' will be ignored. ; [!debug-mode|''debug-mode''] | | > | 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | : Specifies a [[box|00001004011200]] where zettel are stored. During startup, __X__ is incremented, starting with one, until no key is found. This allows to configuring than one box. If no ''box-uri-1'' key is given, the overall effect will be the same as if only ''box-uri-1'' was specified with the value ""dir://.zettel"". In this case, even a key ''box-uri-2'' will be ignored. ; [!debug-mode|''debug-mode''] : If set to [[true|00001006030500]], allows to debug the Zettelstore software (mostly used by Zettelstore developers). Disables any timeout values of the internal web server and does not send some security-related data. Sets [[''log-level''|#log-level]] to ""debug"". Enables [[''runtime-profiling''|#runtime-profiling]]. Do not enable it for a production server. Default: ""false"" ; [!default-dir-box-type|''default-dir-box-type''] : Specifies the default value for the (sub-)type of [[directory boxes|00001004011400#type]], in which Zettel are typically stored. |
︙ | ︙ | |||
116 117 118 119 120 121 122 123 124 125 126 127 128 129 | Default: ""false"" ; [!read-only-mode|''read-only-mode''] : If set to a [[true value|00001006030500]] the Zettelstore service puts into a read-only mode. No changes are possible. Default: ""false"". ; [!secret|''secret''] : A string value to make the communication with external clients strong enough so that sessions of the [[web user interface|00001014000000]] or [[API access token|00001010040700]] cannot be altered by some external unfriendly party. The string must have a length of at least 16 bytes. This value is only needed to be set if [[authentication is enabled|00001010040100]] by setting the key [[''owner''|#owner]] to some user identification value. ; [!token-lifetime-api|''token-lifetime-api''], [!token-lifetime-html|''token-lifetime-html''] : Define lifetime of access tokens in minutes. | > > > > > | 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 | Default: ""false"" ; [!read-only-mode|''read-only-mode''] : If set to a [[true value|00001006030500]] the Zettelstore service puts into a read-only mode. No changes are possible. Default: ""false"". ; [!runtime-profiling|''runtime-profiling''] : A boolean value that enables a web interface to obtain [[runtime profiling information|00001004010200]]. Default: ""false"", but it is set to ""true"" if [[''debug-mode''|#debug-mode]] is enabled. In this case, it cannot be disabled. ; [!secret|''secret''] : A string value to make the communication with external clients strong enough so that sessions of the [[web user interface|00001014000000]] or [[API access token|00001010040700]] cannot be altered by some external unfriendly party. The string must have a length of at least 16 bytes. This value is only needed to be set if [[authentication is enabled|00001010040100]] by setting the key [[''owner''|#owner]] to some user identification value. ; [!token-lifetime-api|''token-lifetime-api''], [!token-lifetime-html|''token-lifetime-html''] : Define lifetime of access tokens in minutes. |
︙ | ︙ |
Added docs/manual/00001004010200.zettel.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | id: 00001004010200 title: Zettelstore runtime profiling role: manual tags: #configuration #manual #zettelstore syntax: zmk created: 20240926144556 modified: 20240926144951 For debugging purposes, you can enable runtime profiling by setting the startup configuration [[''runtime-profiling''|00001004010000#runtime-profiling]]. Typically, a Zettelstore developer will do this. In certain cases, a Zettelstore developer will ask you to enable runtime profiling, because you encountered a hard error. Runtime profiling will generate some data that can be retrieved through the builtin web server. The following URL paths are valid: |=Path|Description |''/rtp/''|Show an index page, where you can navigate to detailed information |''/rtp/allocs''|Show a sampling of all past memory allocations |''/rtp/block''|Show stack traces that led to internal blocking |''/rtp/cmdline''|Show the running Zettelstore command line, with arguments separated by NUL bytes |''/rtp/goroutine''|Show stack traces of all current internal activities |''/rtp/heap''|Show a sampling of memory allocations of live objects |''/rtp/mutex''|Show stack traces of holders of contended mutexes |''/rtp/profile''|Execute a CPU profile |''/rtp/symbol''|Shows function names for given program counter value |''/rtp/trace''|Show trace of execution of the current program |''/rtp/threadcreate''|Show stack traces that led to the creation of new OS threads See documentation for Go standard package [[''net/http/pprof''|https://pkg.go.dev/net/http/pprof]]. |
Changes to kernel/impl/web.go.
︙ | ︙ | |||
78 79 80 81 82 83 84 85 86 87 88 89 90 91 | return "", err } return ap.String(), nil }, true}, kernel.WebMaxRequestSize: {"Max Request Size", parseInt64, true}, kernel.WebPersistentCookie: {"Persistent cookie", parseBool, true}, kernel.WebSecureCookie: {"Secure cookie", parseBool, true}, kernel.WebTokenLifetimeAPI: { "Token lifetime API", makeDurationParser(10*time.Minute, 0, 1*time.Hour), true, }, kernel.WebTokenLifetimeHTML: { | > | 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 | return "", err } return ap.String(), nil }, true}, kernel.WebMaxRequestSize: {"Max Request Size", parseInt64, true}, kernel.WebPersistentCookie: {"Persistent cookie", parseBool, true}, kernel.WebProfiling: {"Runtime profiling", parseBool, true}, kernel.WebSecureCookie: {"Secure cookie", parseBool, true}, kernel.WebTokenLifetimeAPI: { "Token lifetime API", makeDurationParser(10*time.Minute, 0, 1*time.Hour), true, }, kernel.WebTokenLifetimeHTML: { |
︙ | ︙ | |||
107 108 109 110 111 112 113 114 115 116 117 118 119 120 | ws.next = interfaceMap{ kernel.WebAssetDir: "", kernel.WebBaseURL: "http://127.0.0.1:23123/", kernel.WebListenAddress: "127.0.0.1:23123", kernel.WebMaxRequestSize: int64(16 * 1024 * 1024), kernel.WebPersistentCookie: false, kernel.WebSecureCookie: true, kernel.WebTokenLifetimeAPI: 1 * time.Hour, kernel.WebTokenLifetimeHTML: 10 * time.Minute, kernel.WebURLPrefix: "/", } } func makeDurationParser(defDur, minDur, maxDur time.Duration) parseFunc { | > | 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | ws.next = interfaceMap{ kernel.WebAssetDir: "", kernel.WebBaseURL: "http://127.0.0.1:23123/", kernel.WebListenAddress: "127.0.0.1:23123", kernel.WebMaxRequestSize: int64(16 * 1024 * 1024), kernel.WebPersistentCookie: false, kernel.WebSecureCookie: true, kernel.WebProfiling: false, kernel.WebTokenLifetimeAPI: 1 * time.Hour, kernel.WebTokenLifetimeHTML: 10 * time.Minute, kernel.WebURLPrefix: "/", } } func makeDurationParser(defDur, minDur, maxDur time.Duration) parseFunc { |
︙ | ︙ | |||
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 | func (ws *webService) Start(kern *myKernel) error { baseURL := ws.GetNextConfig(kernel.WebBaseURL).(string) listenAddr := ws.GetNextConfig(kernel.WebListenAddress).(string) urlPrefix := ws.GetNextConfig(kernel.WebURLPrefix).(string) persistentCookie := ws.GetNextConfig(kernel.WebPersistentCookie).(bool) secureCookie := ws.GetNextConfig(kernel.WebSecureCookie).(bool) maxRequestSize := ws.GetNextConfig(kernel.WebMaxRequestSize).(int64) if maxRequestSize < 1024 { maxRequestSize = 1024 } if !strings.HasSuffix(baseURL, urlPrefix) { ws.logger.Error().Str("base-url", baseURL).Str("url-prefix", urlPrefix).Msg( "url-prefix is not a suffix of base-url") return errWrongBasePrefix } if lap := netip.MustParseAddrPort(listenAddr); !kern.auth.manager.WithAuth() && !lap.Addr().IsLoopback() { ws.logger.Info().Str("listen", listenAddr).Msg("service may be reached from outside, but authentication is not enabled") } | > > > > > > > > > > > > | | 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 | func (ws *webService) Start(kern *myKernel) error { baseURL := ws.GetNextConfig(kernel.WebBaseURL).(string) listenAddr := ws.GetNextConfig(kernel.WebListenAddress).(string) urlPrefix := ws.GetNextConfig(kernel.WebURLPrefix).(string) persistentCookie := ws.GetNextConfig(kernel.WebPersistentCookie).(bool) secureCookie := ws.GetNextConfig(kernel.WebSecureCookie).(bool) profile := ws.GetNextConfig(kernel.WebProfiling).(bool) maxRequestSize := ws.GetNextConfig(kernel.WebMaxRequestSize).(int64) if maxRequestSize < 1024 { maxRequestSize = 1024 } if !strings.HasSuffix(baseURL, urlPrefix) { ws.logger.Error().Str("base-url", baseURL).Str("url-prefix", urlPrefix).Msg( "url-prefix is not a suffix of base-url") return errWrongBasePrefix } if lap := netip.MustParseAddrPort(listenAddr); !kern.auth.manager.WithAuth() && !lap.Addr().IsLoopback() { ws.logger.Info().Str("listen", listenAddr).Msg("service may be reached from outside, but authentication is not enabled") } sd := impl.ServerData{ Log: ws.logger, ListenAddr: listenAddr, BaseURL: baseURL, URLPrefix: urlPrefix, MaxRequestSize: maxRequestSize, Auth: kern.auth.manager, PersistentCookie: persistentCookie, SecureCookie: secureCookie, Profiling: profile, } srvw := impl.New(sd) err := kern.web.setupServer(srvw, kern.box.manager, kern.auth.manager, &kern.cfg) if err != nil { ws.logger.Error().Err(err).Msg("Unable to create") return err } if kern.core.GetNextConfig(kernel.CoreDebug).(bool) { srvw.SetDebug() |
︙ | ︙ |
Changes to kernel/kernel.go.
︙ | ︙ | |||
189 190 191 192 193 194 195 196 197 198 199 200 201 202 | // Constants for web service keys. const ( WebAssetDir = "asset-dir" WebBaseURL = "base-url" WebListenAddress = "listen" WebPersistentCookie = "persistent" WebMaxRequestSize = "max-request-size" WebSecureCookie = "secure" WebTokenLifetimeAPI = "api-lifetime" WebTokenLifetimeHTML = "html-lifetime" WebURLPrefix = "prefix" ) | > | 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 | // Constants for web service keys. const ( WebAssetDir = "asset-dir" WebBaseURL = "base-url" WebListenAddress = "listen" WebPersistentCookie = "persistent" WebProfiling = "profiling" WebMaxRequestSize = "max-request-size" WebSecureCookie = "secure" WebTokenLifetimeAPI = "api-lifetime" WebTokenLifetimeHTML = "html-lifetime" WebURLPrefix = "prefix" ) |
︙ | ︙ |
Changes to web/server/impl/impl.go.
︙ | ︙ | |||
30 31 32 33 34 35 36 37 38 | log *logger.Logger baseURL string server httpServer router httpRouter persistentCookie bool secureCookie bool } // New creates a new web server. | > > > > > > > > > > > > | | | | | > > > > > > > > | | | 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | log *logger.Logger baseURL string server httpServer router httpRouter persistentCookie bool secureCookie bool } type ServerData struct { Log *logger.Logger ListenAddr string BaseURL string URLPrefix string MaxRequestSize int64 Auth auth.TokenManager PersistentCookie bool SecureCookie bool Profiling bool } // New creates a new web server. func New(sd ServerData) server.Server { srv := myServer{ log: sd.Log, baseURL: sd.BaseURL, persistentCookie: sd.PersistentCookie, secureCookie: sd.SecureCookie, } rd := routerData{ log: sd.Log, urlPrefix: sd.URLPrefix, maxRequestSize: sd.MaxRequestSize, auth: sd.Auth, profiling: sd.Profiling, } srv.router.initializeRouter(rd) srv.server.initializeHTTPServer(sd.ListenAddr, &srv.router) return &srv } func (srv *myServer) Handle(pattern string, handler http.Handler) { srv.router.Handle(pattern, handler) } func (srv *myServer) AddListRoute(key byte, method server.Method, handler http.Handler) { |
︙ | ︙ |
Changes to web/server/impl/router.go.
︙ | ︙ | |||
50 51 52 53 54 55 56 57 58 | reURL *regexp.Regexp listTable routingTable zettelTable routingTable ur server.UserRetriever mux *http.ServeMux maxReqSize int64 } // initializeRouter creates a new, empty router with the given root handler. | > > > > > > > > | | | | | > | > | > | | | | | | 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 | reURL *regexp.Regexp listTable routingTable zettelTable routingTable ur server.UserRetriever mux *http.ServeMux maxReqSize int64 } type routerData struct { log *logger.Logger urlPrefix string maxRequestSize int64 auth auth.TokenManager profiling bool } // initializeRouter creates a new, empty router with the given root handler. func (rt *httpRouter) initializeRouter(rd routerData) { rt.log = rd.log rt.urlPrefix = rd.urlPrefix rt.auth = rd.auth rt.minKey = 255 rt.maxKey = 0 rt.reURL = regexp.MustCompile("^$") rt.mux = http.NewServeMux() rt.maxReqSize = rd.maxRequestSize if rd.profiling { rt.setRuntimeProfiling() } } func (rt *httpRouter) setRuntimeProfiling() { rt.mux.HandleFunc("GET /rtp/", pprof.Index) for _, profile := range rtprf.Profiles() { name := profile.Name() rt.mux.Handle("GET /rtp/"+name, pprof.Handler(name)) } rt.mux.HandleFunc("GET /rtp/cmdline", pprof.Cmdline) rt.mux.HandleFunc("GET /rtp/profile", pprof.Profile) rt.mux.HandleFunc("GET /rtp/symbol", pprof.Symbol) rt.mux.HandleFunc("GET /rtp/trace", pprof.Trace) } func (rt *httpRouter) addRoute(key byte, method server.Method, handler http.Handler, table *routingTable) { // Set minKey and maxKey; re-calculate regexp. if key < rt.minKey || rt.maxKey < key { if key < rt.minKey { rt.minKey = key |
︙ | ︙ |
Changes to www/changes.wiki.
︙ | ︙ | |||
15 16 17 18 19 20 21 22 23 24 25 26 27 28 | (minor: webui, api) * External links are now generatd in shtml and html with attribute rel="external" (previously: class="external"). (minor: webui, api) * Show new format zettel identifier in zettel view, info view and delete view. (minor: webui) <a id="0_18"></a> <h2>Changes for Version 0.18.0 (2024-07-11)</h2> * Remove Sx macro <code>defunconst</code>. Use <code>defun</code> instead. (breaking: webui) * The sz encoding of zettel does not make use of <code>(SPACE)</code> elements any more. Instead, space characters are encoded within the | > > > | 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | (minor: webui, api) * External links are now generatd in shtml and html with attribute rel="external" (previously: class="external"). (minor: webui, api) * Show new format zettel identifier in zettel view, info view and delete view. (minor: webui) * Allow to enable runtime profiling of the software, to be used by developers. (minor) <a id="0_18"></a> <h2>Changes for Version 0.18.0 (2024-07-11)</h2> * Remove Sx macro <code>defunconst</code>. Use <code>defun</code> instead. (breaking: webui) * The sz encoding of zettel does not make use of <code>(SPACE)</code> elements any more. Instead, space characters are encoded within the |
︙ | ︙ |