Skip to content
This repository was archived by the owner on Aug 29, 2025. It is now read-only.

Commit d07b20a

Browse files
committed
Added loggers sample
1 parent e6584c4 commit d07b20a

File tree

10 files changed

+213
-1
lines changed

10 files changed

+213
-1
lines changed

.eslintrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
"globals": {
1414
"SELECT": true,
1515
"INSERT": true,
16+
"UPSERT": true,
1617
"UPDATE": true,
1718
"DELETE": true,
1819
"CREATE": true,

hello/srv/world.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
11
module.exports = class say {
2-
hello(req) { return `Hello ${req.data.to}!` }
2+
hello(req) {
3+
let {to} = req.data
4+
if (to === 'me') to = require('os').userInfo().username
5+
return `Hello ${to}!`
6+
}
37
}

loggers/app/loggers.html

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
<!DOCTYPE html>
2+
<html>
3+
4+
<head>
5+
<title> cds.log </title>
6+
<link rel="stylesheet" href="https://unpkg.com/primitive-ui/dist/css/main.css">
7+
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
8+
<script src="https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.prod.js"></script>
9+
<style>
10+
select { border-color: transparent; padding: 4px 12px; margin: 0px; }
11+
button { padding: 2px 11px; margin: 0px 4px; font: 90% italic; }
12+
</style>
13+
</head>
14+
15+
<body class="small-container" , style="margin-top: 70px;">
16+
<div id='app'>
17+
<h1> Log Levels </h1>
18+
<input type="text" placeholder="Search by ID or Log Level..." @input="fetch">
19+
<table id='loggers'>
20+
<thead>
21+
<th> Module ID </th>
22+
<th> Log Level </th>
23+
</thead>
24+
<tr v-for="each in list">
25+
<td>{{ each.id }}</td>
26+
<td><select v-bind:id="each.id" v-model="each.level" @change="set">
27+
<option>SILENT</option>
28+
<option>ERROR</option>
29+
<option>WARN</option>
30+
<option>INFO</option>
31+
<option>DEBUG</option>
32+
<option>TRACE</option>
33+
</select>
34+
</td>
35+
</tr>
36+
</table>
37+
<h4>Log Format:</h4>
38+
[ <button class="round-button" :class={'muted-button':!format.timestamp} @click="toggle_format" id="timestamp">Timestamp </button>
39+
| <button class="round-button" :class={'muted-button':!format.level} @click="toggle_format" id="level">Log Level </button>
40+
| <button class="round-button" :class={'muted-button':!format.tenant} @click="toggle_format" id="tenant">Tenant </button>
41+
| <button class="round-button" :class={'muted-button':!format.reqid} @click="toggle_format" id="reqid">Request ID </button>
42+
| <button class="round-button" :class={'muted-button':!format.id} @click="toggle_format" id="module">Logger ID </button>
43+
] - <i>log message ...</i>
44+
</div>
45+
</body>
46+
47+
<script>
48+
axios.defaults.headers['Content-Type'] = 'application/json'
49+
axios.defaults.baseURL = '/log'
50+
const loggers = Vue.createApp({ el: '#app',
51+
data() {
52+
return {
53+
format: { timestamp:false, level:false, tenant:false, reqid:false, id:true, },
54+
list: [],
55+
}
56+
},
57+
methods: {
58+
async fetch (eve) {
59+
this.list = (await axios.get (`/Loggers${
60+
eve && eve.target.value ? `?$search=${eve.target.value}` : ''
61+
}`)).data
62+
},
63+
async set (eve) {
64+
const { id, value:level } = eve.target
65+
await axios.put (`/Logger/${id}`, {id,level})
66+
},
67+
async toggle_format (eve) {
68+
this.format[eve.target.id] = !this.format[eve.target.id]
69+
await axios.post (`/format`, this.format)
70+
},
71+
},
72+
}).mount('#app')
73+
loggers.fetch() // initially fill list of loggers
74+
</script>
75+
76+
</html>

loggers/package.json

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"name": "@capire/loggers",
3+
"version": "1.0.0",
4+
"description": "Simple sample on how to dynamically set cds.log levels and formats.",
5+
"files": [
6+
"app",
7+
"srv"
8+
],
9+
"dependencies": {
10+
"@sap/cds": ">=5.9",
11+
"express": "^4.17.1"
12+
},
13+
"scripts": {
14+
"start": "cds run",
15+
"watch": "cds watch"
16+
},
17+
"cds": {
18+
"requires": {
19+
"db": "sql"
20+
}
21+
}
22+
}

loggers/readme.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Dynamically Set `cds.log` Levels and Formats
2+
3+
### Run
4+
5+
```sh
6+
cds watch
7+
```
8+
9+
### Test
10+
11+
Either using the UI through http://localhost:4004/loggers.html, or try the requests in `test/requests.http`

loggers/srv/dummy.cds

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
service Sue {
2+
entity Dummy { key ID: UUID; title: String; }
3+
}

loggers/srv/loggers.cds

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
@rest service LogService {
2+
3+
@readonly entity Loggers : Logger {};
4+
entity Logger {
5+
key id : String;
6+
level : String;
7+
}
8+
9+
action format (
10+
timestamp : Boolean,
11+
level : Boolean,
12+
tenant : Boolean,
13+
reqid : Boolean,
14+
id : Boolean,
15+
);
16+
17+
action debug (logger : String) returns Logger;
18+
action reset (logger : String) returns Logger;
19+
20+
}

loggers/srv/loggers.js

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
const cds = require ('@sap/cds/lib')
2+
const LOG = cds.log('cds.log')
3+
4+
module.exports = class LogService extends cds.Service {
5+
init(){
6+
7+
this.on('GET','Loggers', (req)=>{
8+
let loggers = Object.values(cds.log.loggers).map (_logger)
9+
let {$search} = req._.req.query
10+
if ($search) {
11+
const re = RegExp($search,'i')
12+
loggers = loggers.filter (l => re.test(l.id) || re.test(l.level))
13+
}
14+
return loggers
15+
})
16+
17+
this.on('PUT','Logger', (req)=>{
18+
const {id} = req.params[0] || req.data
19+
if (!id) return req.reject('No logger id specified in request')
20+
return _logger (cds.log (id, req.data))
21+
})
22+
23+
this.on('debug', (req)=>{
24+
const {logger:id} = req.params[0] || req.data
25+
if (!id) return req.reject('No logger id specified in request')
26+
return _logger (cds.log (id, {level:'debug'}))
27+
})
28+
29+
this.on('reset', (req)=>{
30+
const {logger:id} = req.params[0] || req.data
31+
if (!id) return req.reject('No logger id specified in request')
32+
return _logger (cds.log (id, {level:'info'}))
33+
})
34+
35+
this.on('format', (req)=>{
36+
const $ = req.data; LOG.info('format:',$)
37+
// Set format for new loggers constructed subsequently
38+
cds.log.format = (id, level, ...args) => {
39+
const fmt = []
40+
if ($.timestamp) fmt.push ('|', (new Date).toISOString())
41+
if ($.level) fmt.push ('|', _levels[level].padEnd(5))
42+
if ($.tenant) fmt.push ('|', cds.context && cds.context.tenant)
43+
if ($.reqid) fmt.push ('|', cds.context && cds.context.id)
44+
if ($.id) fmt.push ('|', id)
45+
fmt[0] = '[', fmt.push ('] -', ...args)
46+
return fmt
47+
}
48+
// Apply this format to all existing loggers
49+
Object.values(cds.log.loggers).forEach (l => l.setFormat (cds.log.format))
50+
})
51+
}
52+
53+
}
54+
55+
const _logger = ({id,level}) => ({id, level:_levels[level] })
56+
const _levels = [ 'SILENT', 'ERROR', 'WARN', 'INFO', 'DEBUG', 'TRACE' ]

loggers/test/requests.http

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
http://localhost:4004/loggers.html
2+
@body: = Content-Type: application/json\n\n
3+
4+
###
5+
GET http://localhost:4004/log/Loggers
6+
7+
###
8+
PUT http://localhost:4004/log/Logger/sqlite
9+
{{body:}} { "level": "debug" }
10+
11+
###
12+
POST http://localhost:4004/log/debug(logger='sqlite')
13+
14+
###
15+
POST http://localhost:4004/log/reset(logger='sqlite')
16+
17+
### Dummy request to see sqlite debug output
18+
GET http://localhost:4004/sue/Dummy

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"./hello",
1717
"./media",
1818
"./orders",
19+
"./loggers",
1920
"./reviews"
2021
],
2122
"devDependencies": {

0 commit comments

Comments
 (0)