-
Notifications
You must be signed in to change notification settings - Fork 4
/
MiniprofilerGrailsPlugin.groovy
140 lines (116 loc) · 5.27 KB
/
MiniprofilerGrailsPlugin.groovy
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
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
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
import grails.util.Environment
import groovy.util.slurpersupport.GPathResult
import io.jdev.miniprofiler.DefaultProfilerProvider
import io.jdev.miniprofiler.MiniProfiler
import io.jdev.miniprofiler.servlet.ProfilingFilter
import io.jdev.miniprofiler.sql.ProfilingDataSource
import io.jdev.miniprofiler.storage.EhcacheStorage
import net.sf.ehcache.store.MemoryStoreEvictionPolicy
import org.codehaus.groovy.grails.commons.spring.BeanConfiguration
import grails.plugin.miniprofiler.profilerplugin.MiniProfilerAppender
import grails.plugin.miniprofiler.profilerplugin.MiniprofilerCondition
import org.springframework.cache.ehcache.EhCacheFactoryBean
class MiniprofilerGrailsPlugin {
def version = "0.5.0-SNAPSHOT"
def grailsVersion = "1.3 > *"
def loadAfter = ['profiler'] // as we want to modify a couple of things that it creates
def author = "Tom Dunstan"
def authorEmail = "[email protected]"
def title = "Mini Profiler plugin"
def description = '''\
Shows timing and sql query information in a head-up display in a web page, useful for debugging
database and other performance problems.
'''
def documentation = "http://grails.org/plugin/miniprofiler"
def license = 'APACHE'
def scm = [url: 'https://github.com/tomdcc/grails-miniprofiler']
def issueManagement = [system: 'Github', url: 'https://github.com/tomdcc/grails-miniprofiler/issues']
// place right on the outside
def getWebXmlFilterOrder() {
def FilterManager = getClass().getClassLoader().loadClass('grails.plugin.webxml.FilterManager')
[miniProfilerFilter: FilterManager.GRAILS_WEB_REQUEST_POSITION + 1]
}
def doWithWebDescriptor = { webXml ->
if (isProfilingDisabled(application)) return
// Add the profiler filter to the web app.
def filterDef = webXml.'filter'
filterDef[filterDef.size() - 1] + {
'filter' {
'filter-name'('miniProfilerFilter')
'filter-class'(ProfilingFilter.name)
}
}
// This filter should come before the standard profiler filter, otherwise we'll miss some events
def filterMapping = webXml.'filter-mapping'.find { it.'filter-name'.text() == "charEncodingFilter" }
filterMapping + {
'filter-mapping' {
'filter-name'("miniProfilerFilter")
'url-pattern'("/*")
}
}
// now the fun part - override the grails page filter with one which profiles layout rendering
try {
// this class exists in grails 2.0-2.3, and replacing it with our profiling one
// is how we measure how long a layout takes.
// no solution for grails 2.4 yet
Class.forName('org.codehaus.groovy.grails.web.sitemesh.GrailsPageFilter')
def pageFilterMapping = webXml.'filter'.find { it.'filter-name'.text() == 'sitemesh' }
if(pageFilterMapping) { // only do this if we can find sitemesh - someone may have removed it
def filterClassNode = pageFilterMapping.'filter-class'[0]
def newFilterClass = 'grails.plugin.miniprofiler.sitemesh.grails20.ProfilingGrailsPageFilter'
if (filterClassNode instanceof GPathResult) {
filterClassNode.replaceBody(newFilterClass)
} else {
filterClassNode.setTextContent(newFilterClass)
}
}
} catch(ClassNotFoundException e) {
// not grails 2.0-2.3, so don't try to replace it - we just won't have layout timing
}
}
private boolean isProfilingDisabled(application) {
application.config.grails?.profiler?.disable as boolean
}
def doWithSpring = {
if (isProfilingDisabled(application)) return
// use an in-memory ehcache by default
profilingCache(EhCacheFactoryBean) {
cacheName = 'miniprofilerCache'
maxElementsInMemory = 1000
maxElementsOnDisk = 0
memoryStoreEvictionPolicy = 'LRU'
overflowToDisk = false
eternal = false
timeToLive = 3600
}
profilerStorage(EhcacheStorage, profilingCache)
// main profiler provider
profilerProvider(DefaultProfilerProvider) {
storage = profilerStorage
}
// replace data source with our proxying one, if it's there
BeanConfiguration dataSourceConfig = springConfig.getBeanConfig('dataSource')
if(dataSourceConfig) {
springConfig.addBeanConfiguration("dataSourceOriginal", dataSourceConfig)
dataSource(ProfilingDataSource, ref('dataSourceOriginal'), profilerProvider)
}
// profiler plugin related stuff below
// just switch profiling on for all requests by default
profilerCondition(MiniprofilerCondition)
// set up our profiler appender to capture output from the profiler
// plugin and pipe it through a mini profiler session instead
miniProfilerAppender(MiniProfilerAppender) {
profilerProvider = ref('profilerProvider')
}
}
def doWithApplicationContext = { applicationContext ->
if (isProfilingDisabled(application)) return
def profilerProvider = applicationContext.profilerProvider
// make available in static contexts, just in case someone needs it
MiniProfiler.profilerProvider = profilerProvider
// set name to have grails env name as well
profilerProvider.machineName = "$profilerProvider.machineName($Environment.current.name)"
// only send profiler data to miniprofiler, don't log to file etc
applicationContext.profilerLog.appenderNames = ['miniProfilerAppender']
}
}